diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 8c4ef98..995ed40 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -1,11 +1,26 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('PIXI.Texture', function ()
{
it('should register Texture from Loader', function ()
{
- const URL = 'foo.png';
- const NAME = 'bar';
+ cleanCache();
+
const image = new Image();
const texture = PIXI.Texture.fromLoader(image, URL, NAME);
@@ -16,4 +31,100 @@
expect(PIXI.utils.TextureCache[URL]).to.equal(texture);
expect(PIXI.utils.BaseTextureCache[URL]).to.equal(texture.baseTexture);
});
+
+ it('should remove Texture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ texture.destroy();
+ expect(texture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should be added to the texture cache correctly, '
+ + 'and should remove only itself, not effecting the base texture and its cache', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
+ PIXI.Texture.addToCache(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.removeFromCache(NAME);
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ 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();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(texture);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove Texture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(NAME);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ });
});
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 8c4ef98..995ed40 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -1,11 +1,26 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('PIXI.Texture', function ()
{
it('should register Texture from Loader', function ()
{
- const URL = 'foo.png';
- const NAME = 'bar';
+ cleanCache();
+
const image = new Image();
const texture = PIXI.Texture.fromLoader(image, URL, NAME);
@@ -16,4 +31,100 @@
expect(PIXI.utils.TextureCache[URL]).to.equal(texture);
expect(PIXI.utils.BaseTextureCache[URL]).to.equal(texture.baseTexture);
});
+
+ it('should remove Texture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ texture.destroy();
+ expect(texture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should be added to the texture cache correctly, '
+ + 'and should remove only itself, not effecting the base texture and its cache', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
+ PIXI.Texture.addToCache(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.removeFromCache(NAME);
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ 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();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(texture);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove Texture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(NAME);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ });
});
diff --git a/test/core/Ticker.js b/test/core/Ticker.js
new file mode 100644
index 0000000..188770f
--- /dev/null
+++ b/test/core/Ticker.js
@@ -0,0 +1,322 @@
+'use strict';
+
+const Ticker = PIXI.ticker.Ticker;
+const shared = PIXI.ticker.shared;
+
+describe('PIXI.ticker.Ticker', function ()
+{
+ before(function ()
+ {
+ this.length = (ticker) =>
+ {
+ ticker = ticker || shared;
+
+ if (!ticker._head || !ticker._head.next)
+ {
+ return 0;
+ }
+
+ let listener = ticker._head.next;
+ let i = 0;
+
+ while (listener)
+ {
+ listener = listener.next;
+ i++;
+ }
+
+ return i;
+ };
+ });
+
+ it('should be available', function ()
+ {
+ expect(Ticker).to.be.a.function;
+ expect(shared).to.be.an.instanceof(Ticker);
+ });
+
+ it('should create a new ticker and destroy it', function ()
+ {
+ const ticker = new Ticker();
+
+ ticker.start();
+
+ const listener = sinon.spy();
+
+ expect(this.length(ticker)).to.equal(0);
+
+ ticker.add(listener);
+
+ expect(this.length(ticker)).to.equal(1);
+
+ ticker.destroy();
+
+ expect(ticker._head).to.be.null;
+ expect(ticker.started).to.be.false;
+ expect(this.length(ticker)).to.equal(0);
+ });
+
+ it('should protect destroying shared ticker', function ()
+ {
+ shared.destroy();
+ expect(shared._head).to.not.be.null;
+ expect(shared.started).to.be.true;
+ });
+
+ it('should add and remove listener', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener);
+
+ expect(this.length()).to.equal(length + 1);
+
+ shared.remove(listener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should update a listener', function ()
+ {
+ const listener = sinon.spy();
+
+ shared.add(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ });
+
+ it('should update a listener twice and remove once', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener).add(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length + 2);
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should respect priority order', function ()
+ {
+ const length = this.length();
+ const listener1 = sinon.spy();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.LOW)
+ .add(listener4, null, PIXI.UPDATE_PRIORITY.INTERACTION)
+ .add(listener3, null, PIXI.UPDATE_PRIORITY.HIGH)
+ .add(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 4);
+
+ sinon.assert.callOrder(listener4, listener3, listener2, listener1);
+
+ shared.remove(listener1)
+ .remove(listener2)
+ .remove(listener3)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should auto-remove once listeners', function ()
+ {
+ const length = this.length();
+ const listener = sinon.spy();
+
+ shared.addOnce(listener);
+
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should call inserted item with a lower priority', function ()
+ {
+ const length = this.length();
+ const lowListener = sinon.spy();
+ const highListener = sinon.spy();
+ const mainListener = sinon.spy(() =>
+ {
+ shared.add(highListener, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(lowListener, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(mainListener, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 3);
+
+ expect(mainListener.calledOnce).to.be.true;
+ expect(lowListener.calledOnce).to.be.true;
+ expect(highListener.calledOnce).to.be.false;
+
+ shared.remove(mainListener)
+ .remove(highListener)
+ .remove(lowListener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove emit low-priority item during emit', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself on emit after adding new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.remove(listener1);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself before, still calling new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener1);
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove items before and after current priority', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.add(listener4, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener2)
+ .remove(listener3);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 2);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.update();
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledTwice).to.be.true;
+ expect(listener1.calledTwice).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should destroy on listener', function (done)
+ {
+ const ticker = new Ticker();
+ const listener2 = sinon.spy();
+ const listener = sinon.spy(() =>
+ {
+ ticker.destroy();
+ setTimeout(() =>
+ {
+ expect(listener2.called).to.be.false;
+ expect(listener.calledOnce).to.be.true;
+ done();
+ }, 0);
+ });
+
+ ticker.add(listener);
+ ticker.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ ticker.start();
+ });
+});
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 8c4ef98..995ed40 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -1,11 +1,26 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('PIXI.Texture', function ()
{
it('should register Texture from Loader', function ()
{
- const URL = 'foo.png';
- const NAME = 'bar';
+ cleanCache();
+
const image = new Image();
const texture = PIXI.Texture.fromLoader(image, URL, NAME);
@@ -16,4 +31,100 @@
expect(PIXI.utils.TextureCache[URL]).to.equal(texture);
expect(PIXI.utils.BaseTextureCache[URL]).to.equal(texture.baseTexture);
});
+
+ it('should remove Texture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ texture.destroy();
+ expect(texture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should be added to the texture cache correctly, '
+ + 'and should remove only itself, not effecting the base texture and its cache', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
+ PIXI.Texture.addToCache(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.removeFromCache(NAME);
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ 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();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(texture);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove Texture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(NAME);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ });
});
diff --git a/test/core/Ticker.js b/test/core/Ticker.js
new file mode 100644
index 0000000..188770f
--- /dev/null
+++ b/test/core/Ticker.js
@@ -0,0 +1,322 @@
+'use strict';
+
+const Ticker = PIXI.ticker.Ticker;
+const shared = PIXI.ticker.shared;
+
+describe('PIXI.ticker.Ticker', function ()
+{
+ before(function ()
+ {
+ this.length = (ticker) =>
+ {
+ ticker = ticker || shared;
+
+ if (!ticker._head || !ticker._head.next)
+ {
+ return 0;
+ }
+
+ let listener = ticker._head.next;
+ let i = 0;
+
+ while (listener)
+ {
+ listener = listener.next;
+ i++;
+ }
+
+ return i;
+ };
+ });
+
+ it('should be available', function ()
+ {
+ expect(Ticker).to.be.a.function;
+ expect(shared).to.be.an.instanceof(Ticker);
+ });
+
+ it('should create a new ticker and destroy it', function ()
+ {
+ const ticker = new Ticker();
+
+ ticker.start();
+
+ const listener = sinon.spy();
+
+ expect(this.length(ticker)).to.equal(0);
+
+ ticker.add(listener);
+
+ expect(this.length(ticker)).to.equal(1);
+
+ ticker.destroy();
+
+ expect(ticker._head).to.be.null;
+ expect(ticker.started).to.be.false;
+ expect(this.length(ticker)).to.equal(0);
+ });
+
+ it('should protect destroying shared ticker', function ()
+ {
+ shared.destroy();
+ expect(shared._head).to.not.be.null;
+ expect(shared.started).to.be.true;
+ });
+
+ it('should add and remove listener', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener);
+
+ expect(this.length()).to.equal(length + 1);
+
+ shared.remove(listener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should update a listener', function ()
+ {
+ const listener = sinon.spy();
+
+ shared.add(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ });
+
+ it('should update a listener twice and remove once', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener).add(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length + 2);
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should respect priority order', function ()
+ {
+ const length = this.length();
+ const listener1 = sinon.spy();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.LOW)
+ .add(listener4, null, PIXI.UPDATE_PRIORITY.INTERACTION)
+ .add(listener3, null, PIXI.UPDATE_PRIORITY.HIGH)
+ .add(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 4);
+
+ sinon.assert.callOrder(listener4, listener3, listener2, listener1);
+
+ shared.remove(listener1)
+ .remove(listener2)
+ .remove(listener3)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should auto-remove once listeners', function ()
+ {
+ const length = this.length();
+ const listener = sinon.spy();
+
+ shared.addOnce(listener);
+
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should call inserted item with a lower priority', function ()
+ {
+ const length = this.length();
+ const lowListener = sinon.spy();
+ const highListener = sinon.spy();
+ const mainListener = sinon.spy(() =>
+ {
+ shared.add(highListener, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(lowListener, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(mainListener, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 3);
+
+ expect(mainListener.calledOnce).to.be.true;
+ expect(lowListener.calledOnce).to.be.true;
+ expect(highListener.calledOnce).to.be.false;
+
+ shared.remove(mainListener)
+ .remove(highListener)
+ .remove(lowListener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove emit low-priority item during emit', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself on emit after adding new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.remove(listener1);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself before, still calling new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener1);
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove items before and after current priority', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.add(listener4, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener2)
+ .remove(listener3);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 2);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.update();
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledTwice).to.be.true;
+ expect(listener1.calledTwice).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should destroy on listener', function (done)
+ {
+ const ticker = new Ticker();
+ const listener2 = sinon.spy();
+ const listener = sinon.spy(() =>
+ {
+ ticker.destroy();
+ setTimeout(() =>
+ {
+ expect(listener2.called).to.be.false;
+ expect(listener.calledOnce).to.be.true;
+ done();
+ }, 0);
+ });
+
+ ticker.add(listener);
+ ticker.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ ticker.start();
+ });
+});
diff --git a/test/core/index.js b/test/core/index.js
index 8530131..28636ab 100755
--- a/test/core/index.js
+++ b/test/core/index.js
@@ -17,6 +17,7 @@
require('./util');
require('./Plane');
require('./Point');
+require('./Polygon');
require('./ObservablePoint');
require('./Matrix');
require('./Rectangle');
@@ -26,5 +27,7 @@
require('./SpriteRenderer');
require('./WebGLRenderer');
require('./Ellipse');
+require('./BaseTexture');
require('./Texture');
+require('./Ticker');
require('./filters');
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 8c4ef98..995ed40 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -1,11 +1,26 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('PIXI.Texture', function ()
{
it('should register Texture from Loader', function ()
{
- const URL = 'foo.png';
- const NAME = 'bar';
+ cleanCache();
+
const image = new Image();
const texture = PIXI.Texture.fromLoader(image, URL, NAME);
@@ -16,4 +31,100 @@
expect(PIXI.utils.TextureCache[URL]).to.equal(texture);
expect(PIXI.utils.BaseTextureCache[URL]).to.equal(texture.baseTexture);
});
+
+ it('should remove Texture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ texture.destroy();
+ expect(texture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should be added to the texture cache correctly, '
+ + 'and should remove only itself, not effecting the base texture and its cache', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
+ PIXI.Texture.addToCache(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.removeFromCache(NAME);
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ 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();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(texture);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove Texture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(NAME);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ });
});
diff --git a/test/core/Ticker.js b/test/core/Ticker.js
new file mode 100644
index 0000000..188770f
--- /dev/null
+++ b/test/core/Ticker.js
@@ -0,0 +1,322 @@
+'use strict';
+
+const Ticker = PIXI.ticker.Ticker;
+const shared = PIXI.ticker.shared;
+
+describe('PIXI.ticker.Ticker', function ()
+{
+ before(function ()
+ {
+ this.length = (ticker) =>
+ {
+ ticker = ticker || shared;
+
+ if (!ticker._head || !ticker._head.next)
+ {
+ return 0;
+ }
+
+ let listener = ticker._head.next;
+ let i = 0;
+
+ while (listener)
+ {
+ listener = listener.next;
+ i++;
+ }
+
+ return i;
+ };
+ });
+
+ it('should be available', function ()
+ {
+ expect(Ticker).to.be.a.function;
+ expect(shared).to.be.an.instanceof(Ticker);
+ });
+
+ it('should create a new ticker and destroy it', function ()
+ {
+ const ticker = new Ticker();
+
+ ticker.start();
+
+ const listener = sinon.spy();
+
+ expect(this.length(ticker)).to.equal(0);
+
+ ticker.add(listener);
+
+ expect(this.length(ticker)).to.equal(1);
+
+ ticker.destroy();
+
+ expect(ticker._head).to.be.null;
+ expect(ticker.started).to.be.false;
+ expect(this.length(ticker)).to.equal(0);
+ });
+
+ it('should protect destroying shared ticker', function ()
+ {
+ shared.destroy();
+ expect(shared._head).to.not.be.null;
+ expect(shared.started).to.be.true;
+ });
+
+ it('should add and remove listener', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener);
+
+ expect(this.length()).to.equal(length + 1);
+
+ shared.remove(listener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should update a listener', function ()
+ {
+ const listener = sinon.spy();
+
+ shared.add(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ });
+
+ it('should update a listener twice and remove once', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener).add(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length + 2);
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should respect priority order', function ()
+ {
+ const length = this.length();
+ const listener1 = sinon.spy();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.LOW)
+ .add(listener4, null, PIXI.UPDATE_PRIORITY.INTERACTION)
+ .add(listener3, null, PIXI.UPDATE_PRIORITY.HIGH)
+ .add(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 4);
+
+ sinon.assert.callOrder(listener4, listener3, listener2, listener1);
+
+ shared.remove(listener1)
+ .remove(listener2)
+ .remove(listener3)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should auto-remove once listeners', function ()
+ {
+ const length = this.length();
+ const listener = sinon.spy();
+
+ shared.addOnce(listener);
+
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should call inserted item with a lower priority', function ()
+ {
+ const length = this.length();
+ const lowListener = sinon.spy();
+ const highListener = sinon.spy();
+ const mainListener = sinon.spy(() =>
+ {
+ shared.add(highListener, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(lowListener, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(mainListener, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 3);
+
+ expect(mainListener.calledOnce).to.be.true;
+ expect(lowListener.calledOnce).to.be.true;
+ expect(highListener.calledOnce).to.be.false;
+
+ shared.remove(mainListener)
+ .remove(highListener)
+ .remove(lowListener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove emit low-priority item during emit', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself on emit after adding new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.remove(listener1);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself before, still calling new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener1);
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove items before and after current priority', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.add(listener4, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener2)
+ .remove(listener3);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 2);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.update();
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledTwice).to.be.true;
+ expect(listener1.calledTwice).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should destroy on listener', function (done)
+ {
+ const ticker = new Ticker();
+ const listener2 = sinon.spy();
+ const listener = sinon.spy(() =>
+ {
+ ticker.destroy();
+ setTimeout(() =>
+ {
+ expect(listener2.called).to.be.false;
+ expect(listener.calledOnce).to.be.true;
+ done();
+ }, 0);
+ });
+
+ ticker.add(listener);
+ ticker.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ ticker.start();
+ });
+});
diff --git a/test/core/index.js b/test/core/index.js
index 8530131..28636ab 100755
--- a/test/core/index.js
+++ b/test/core/index.js
@@ -17,6 +17,7 @@
require('./util');
require('./Plane');
require('./Point');
+require('./Polygon');
require('./ObservablePoint');
require('./Matrix');
require('./Rectangle');
@@ -26,5 +27,7 @@
require('./SpriteRenderer');
require('./WebGLRenderer');
require('./Ellipse');
+require('./BaseTexture');
require('./Texture');
+require('./Ticker');
require('./filters');
diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js
index c87fa5c..31385b9 100644
--- a/test/interaction/InteractionManager.js
+++ b/test/interaction/InteractionManager.js
@@ -1198,4 +1198,85 @@
expect(pointer.interaction.activeInteractionData[42]).to.be.undefined;
});
});
+
+ describe('hitTest()', function ()
+ {
+ it('should return hit', 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.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10));
+
+ expect(hit).to.equal(graphics);
+ });
+
+ it('should return null if not hit', 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.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(60, 60));
+
+ expect(hit).to.be.null;
+ });
+
+ it('should return top thing that was hit', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const behind = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(behind);
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ behind.beginFill(0xFFFFFF);
+ behind.drawRect(0, 0, 50, 50);
+ behind.interactive = true;
+
+ pointer.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10));
+
+ expect(hit).to.equal(graphics);
+ });
+
+ it('should return hit when passing in root', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const behind = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(behind);
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ behind.beginFill(0xFFFFFF);
+ behind.drawRect(0, 0, 50, 50);
+ behind.interactive = true;
+
+ pointer.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10), behind);
+
+ expect(hit).to.equal(behind);
+ });
+ });
});
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 8c4ef98..995ed40 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -1,11 +1,26 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('PIXI.Texture', function ()
{
it('should register Texture from Loader', function ()
{
- const URL = 'foo.png';
- const NAME = 'bar';
+ cleanCache();
+
const image = new Image();
const texture = PIXI.Texture.fromLoader(image, URL, NAME);
@@ -16,4 +31,100 @@
expect(PIXI.utils.TextureCache[URL]).to.equal(texture);
expect(PIXI.utils.BaseTextureCache[URL]).to.equal(texture.baseTexture);
});
+
+ it('should remove Texture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ texture.destroy();
+ expect(texture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should be added to the texture cache correctly, '
+ + 'and should remove only itself, not effecting the base texture and its cache', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
+ PIXI.Texture.addToCache(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.removeFromCache(NAME);
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ 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();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(texture);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove Texture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(NAME);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ });
});
diff --git a/test/core/Ticker.js b/test/core/Ticker.js
new file mode 100644
index 0000000..188770f
--- /dev/null
+++ b/test/core/Ticker.js
@@ -0,0 +1,322 @@
+'use strict';
+
+const Ticker = PIXI.ticker.Ticker;
+const shared = PIXI.ticker.shared;
+
+describe('PIXI.ticker.Ticker', function ()
+{
+ before(function ()
+ {
+ this.length = (ticker) =>
+ {
+ ticker = ticker || shared;
+
+ if (!ticker._head || !ticker._head.next)
+ {
+ return 0;
+ }
+
+ let listener = ticker._head.next;
+ let i = 0;
+
+ while (listener)
+ {
+ listener = listener.next;
+ i++;
+ }
+
+ return i;
+ };
+ });
+
+ it('should be available', function ()
+ {
+ expect(Ticker).to.be.a.function;
+ expect(shared).to.be.an.instanceof(Ticker);
+ });
+
+ it('should create a new ticker and destroy it', function ()
+ {
+ const ticker = new Ticker();
+
+ ticker.start();
+
+ const listener = sinon.spy();
+
+ expect(this.length(ticker)).to.equal(0);
+
+ ticker.add(listener);
+
+ expect(this.length(ticker)).to.equal(1);
+
+ ticker.destroy();
+
+ expect(ticker._head).to.be.null;
+ expect(ticker.started).to.be.false;
+ expect(this.length(ticker)).to.equal(0);
+ });
+
+ it('should protect destroying shared ticker', function ()
+ {
+ shared.destroy();
+ expect(shared._head).to.not.be.null;
+ expect(shared.started).to.be.true;
+ });
+
+ it('should add and remove listener', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener);
+
+ expect(this.length()).to.equal(length + 1);
+
+ shared.remove(listener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should update a listener', function ()
+ {
+ const listener = sinon.spy();
+
+ shared.add(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ });
+
+ it('should update a listener twice and remove once', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener).add(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length + 2);
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should respect priority order', function ()
+ {
+ const length = this.length();
+ const listener1 = sinon.spy();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.LOW)
+ .add(listener4, null, PIXI.UPDATE_PRIORITY.INTERACTION)
+ .add(listener3, null, PIXI.UPDATE_PRIORITY.HIGH)
+ .add(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 4);
+
+ sinon.assert.callOrder(listener4, listener3, listener2, listener1);
+
+ shared.remove(listener1)
+ .remove(listener2)
+ .remove(listener3)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should auto-remove once listeners', function ()
+ {
+ const length = this.length();
+ const listener = sinon.spy();
+
+ shared.addOnce(listener);
+
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should call inserted item with a lower priority', function ()
+ {
+ const length = this.length();
+ const lowListener = sinon.spy();
+ const highListener = sinon.spy();
+ const mainListener = sinon.spy(() =>
+ {
+ shared.add(highListener, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(lowListener, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(mainListener, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 3);
+
+ expect(mainListener.calledOnce).to.be.true;
+ expect(lowListener.calledOnce).to.be.true;
+ expect(highListener.calledOnce).to.be.false;
+
+ shared.remove(mainListener)
+ .remove(highListener)
+ .remove(lowListener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove emit low-priority item during emit', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself on emit after adding new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.remove(listener1);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself before, still calling new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener1);
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove items before and after current priority', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.add(listener4, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener2)
+ .remove(listener3);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 2);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.update();
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledTwice).to.be.true;
+ expect(listener1.calledTwice).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should destroy on listener', function (done)
+ {
+ const ticker = new Ticker();
+ const listener2 = sinon.spy();
+ const listener = sinon.spy(() =>
+ {
+ ticker.destroy();
+ setTimeout(() =>
+ {
+ expect(listener2.called).to.be.false;
+ expect(listener.calledOnce).to.be.true;
+ done();
+ }, 0);
+ });
+
+ ticker.add(listener);
+ ticker.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ ticker.start();
+ });
+});
diff --git a/test/core/index.js b/test/core/index.js
index 8530131..28636ab 100755
--- a/test/core/index.js
+++ b/test/core/index.js
@@ -17,6 +17,7 @@
require('./util');
require('./Plane');
require('./Point');
+require('./Polygon');
require('./ObservablePoint');
require('./Matrix');
require('./Rectangle');
@@ -26,5 +27,7 @@
require('./SpriteRenderer');
require('./WebGLRenderer');
require('./Ellipse');
+require('./BaseTexture');
require('./Texture');
+require('./Ticker');
require('./filters');
diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js
index c87fa5c..31385b9 100644
--- a/test/interaction/InteractionManager.js
+++ b/test/interaction/InteractionManager.js
@@ -1198,4 +1198,85 @@
expect(pointer.interaction.activeInteractionData[42]).to.be.undefined;
});
});
+
+ describe('hitTest()', function ()
+ {
+ it('should return hit', 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.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10));
+
+ expect(hit).to.equal(graphics);
+ });
+
+ it('should return null if not hit', 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.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(60, 60));
+
+ expect(hit).to.be.null;
+ });
+
+ it('should return top thing that was hit', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const behind = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(behind);
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ behind.beginFill(0xFFFFFF);
+ behind.drawRect(0, 0, 50, 50);
+ behind.interactive = true;
+
+ pointer.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10));
+
+ expect(hit).to.equal(graphics);
+ });
+
+ it('should return hit when passing in root', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const behind = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(behind);
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ behind.beginFill(0xFFFFFF);
+ behind.drawRect(0, 0, 50, 50);
+ behind.interactive = true;
+
+ pointer.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10), behind);
+
+ expect(hit).to.equal(behind);
+ });
+ });
});
diff --git a/test/loaders/spritesheetParser.js b/test/loaders/spritesheetParser.js
index 79f8c15..4d54805 100644
--- a/test/loaders/spritesheetParser.js
+++ b/test/loaders/spritesheetParser.js
@@ -60,11 +60,46 @@
.that.is.an.instanceof(PIXI.Texture);
});
+ it('should build the image url', function ()
+ {
+ function getResourcePath(url, image)
+ {
+ return PIXI.loaders.getResourcePath({
+ url,
+ data: { meta: { image } },
+ });
+ }
+
+ let result = getResourcePath('http://some.com/spritesheet.json', 'img.png');
+
+ expect(result).to.be.equals('http://some.com/img.png');
+
+ result = getResourcePath('http://some.com/some/dir/spritesheet.json', 'img.png');
+ expect(result).to.be.equals('http://some.com/some/dir/img.png');
+
+ result = getResourcePath('http://some.com/some/dir/spritesheet.json', './img.png');
+ expect(result).to.be.equals('http://some.com/some/dir/img.png');
+
+ result = getResourcePath('http://some.com/some/dir/spritesheet.json', '../img.png');
+ expect(result).to.be.equals('http://some.com/some/img.png');
+
+ result = getResourcePath('/spritesheet.json', 'img.png');
+ expect(result).to.be.equals('/img.png');
+
+ result = getResourcePath('/some/dir/spritesheet.json', 'img.png');
+ expect(result).to.be.equals('/some/dir/img.png');
+
+ result = getResourcePath('/some/dir/spritesheet.json', './img.png');
+ expect(result).to.be.equals('/some/dir/img.png');
+
+ result = getResourcePath('/some/dir/spritesheet.json', '../img.png');
+ expect(result).to.be.equals('/some/img.png');
+ });
+
// TODO: Test that rectangles are created correctly.
// TODO: Test that bathc processing works correctly.
// TODO: Test that resolution processing works correctly.
// TODO: Test that metadata is honored.
- // TODO: Test data-url code paths.
});
function createMockResource(type, data)
diff --git a/.travis.yml b/.travis.yml
index 3df8b22..14de334 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -110,3 +110,43 @@
on:
all_branches: true
condition: $TRAVIS_TAG
+ # Deploy config for latest release
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: dist
+ upload-dir: "release"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: docs
+ upload-dir: "release/docs"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
+ - provider: s3
+ access_key_id: $S3_ACCESS_KEY_ID
+ secret_access_key: $S3_SECRET_ACCESS_KEY
+ bucket: "pixi.js"
+ skip_cleanup: true
+ acl: public_read
+ region: eu-west-1
+ cache_control: "max-age=1209600"
+ local_dir: coverage
+ upload-dir: "release/coverage"
+ on:
+ all_branches: true
+ condition: $TRAVIS_TAG
\ No newline at end of file
diff --git a/README.md b/README.md
index d26fadd..3b85f85 100644
--- a/README.md
+++ b/README.md
@@ -36,22 +36,14 @@
- Wiki: Other misc tutorials and resources are [on the Wiki](https://github.com/pixijs/pixi.js/wiki/Resources).
### Community ###
-- Forums: Check out the [forum] (http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow] (http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
+- Forums: Check out the [forum](http://www.html5gamedevs.com/forum/15-pixijs/) and [Stackoverflow](http://stackoverflow.com/search?q=pixi.js), both friendly places to ask your pixi questions.
- Inspiration: Check out the [gallery](http://www.pixijs.com/gallery) to see some of the amazing things people have created!
- Chat: You can join us on [Gitter](https://gitter.im/pixijs/pixi.js) To chat about Pixi. We also now have a Slack channel. If you would like to join it please Send me an email (mat@goodboydigital.com) and I will invite you in.
### Setup ###
-It's easy to get started with Pixi.js! Simply grab the pre-built versions from here:
-
-Release Branch - Nice and stable Pixi.js
-- Unminified: [http://pixijs.download/release/pixi.js]
-- Minified: [http://pixijs.download/release/pixi.min.js]
-
-Develop Branch - The bleeding edge version of Pixi.js
-- Unminified: [http://pixijs.download/dev/pixi.js]
-- Minified: [http://pixijs.download/dev/pixi.min.js]
+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.
@@ -70,10 +62,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.2.2` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
@@ -130,7 +122,7 @@
// and the root stage PIXI.Container.
var app = new PIXI.Application();
-// The application will create a canvas element for you that you
+// The application will create a canvas element for you that you
// can then insert into the DOM.
document.body.appendChild(app.view);
@@ -150,7 +142,7 @@
// Add the bunny to the scene we are building.
app.stage.addChild(bunny);
-
+
// Listen for frame updates
app.ticker.add(function() {
// each frame we spin the bunny around a bit
diff --git a/bower.json b/bower.json
deleted file mode 100644
index 41b668c..0000000
--- a/bower.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "name": "pixi.js",
- "main": "dist/pixi.min.js",
- "ignore": [
- "**/.*",
- "docs",
- "node_modules",
- "bower_components",
- "test",
- "scripts",
- "bower.json",
- "inch.json"
- ],
- "dependencies": {
- },
- "devDependencies": {
- }
-}
diff --git a/package.json b/package.json
index d371ae8..ea9a0c4 100644
--- a/package.json
+++ b/package.json
@@ -40,11 +40,11 @@
"lib": "babel src --out-dir lib -s",
"predocs": "rimraf docs/**",
"docs": "jsdoc -c scripts/jsdoc.conf.json -R README.md",
- "publish:patch": "npm version patch --no-git-tag-version && npm publish",
- "publish:minor": "npm version minor --no-git-tag-version && npm publish",
- "publish:major": "npm version major --no-git-tag-version && npm publish",
+ "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",
- "postpublish": "node scripts/release.js"
+ "postpublish": "git push && git push --tags"
},
"files": [
"dist/",
@@ -73,7 +73,6 @@
"electron": "^1.4.15",
"eslint": "^3.5.0",
"floss": "^2.0.1",
- "gh-pages": "^0.11.0",
"jaguarjs-jsdoc": "^1.0.1",
"js-md5": "^0.4.1",
"jsdoc": "^3.4.2",
diff --git a/scripts/jsdoc.conf.json b/scripts/jsdoc.conf.json
index 553e61c..3f52180 100644
--- a/scripts/jsdoc.conf.json
+++ b/scripts/jsdoc.conf.json
@@ -43,7 +43,7 @@
},
"markdown" : {
"parser" : "gfm",
- "hardwrap" : true
+ "hardwrap" : false
},
"opts": {
"encoding" : "utf8",
diff --git a/scripts/release.js b/scripts/release.js
deleted file mode 100644
index 156620c..0000000
--- a/scripts/release.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-'use strict';
-
-// Publish script to push releases of the bin files
-// the normally are gitignored
-const ghpages = require('gh-pages');
-const path = require('path');
-const packageInfo = require(path.join(__dirname, '..', 'package.json'));
-
-const options = {
- src: [
- 'dist/**/*',
- 'lib/**/*',
- 'src/**/*',
- 'scripts/*',
- 'scripts/renders/*',
- 'test/**/*',
- '*.json',
- '*.md',
- 'LICENSE',
- '.eslintrc',
- '.editorconfig',
- '.travis.yml',
- '.babelrc',
- ],
- dotfiles: true,
- branch: 'release',
- message: packageInfo.version,
- logger: console.log.bind(console),
-};
-
-ghpages.publish(process.cwd(), options, (err) =>
-{
- if (err)
- {
- console.log(err);
- process.exit(1);
-
- return;
- }
-
- process.exit(0);
-});
diff --git a/src/accessibility/AccessibilityManager.js b/src/accessibility/AccessibilityManager.js
index f16bcb1..3c440df 100644
--- a/src/accessibility/AccessibilityManager.js
+++ b/src/accessibility/AccessibilityManager.js
@@ -21,12 +21,12 @@
const DIV_HOOK_ZINDEX = 2;
/**
- * The Accessibility manager reacreates the ability to tab and and have content read by screen
+ * The Accessibility manager recreates the ability to tab and have content read by screen
* readers. This is very important as it can possibly help people with disabilities access pixi
* content.
*
* Much like interaction any DisplayObject can be made accessible. This manager will map the
- * events as if the mouse was being used, minimizing the efferot required to implement.
+ * events as if the mouse was being used, minimizing the effort required to implement.
*
* An instance of this class is automatically created by default, and can be found at renderer.plugins.accessibility
*
@@ -80,7 +80,7 @@
this.renderId = 0;
/**
- * Setting this to true will visually show the divs
+ * Setting this to true will visually show the divs.
*
* @type {boolean}
*/
@@ -110,7 +110,7 @@
this._onMouseMove = this._onMouseMove.bind(this);
/**
- * stores the state of the manager. If there are no accessible objects or the mouse is moving the will be false.
+ * stores the state of the manager. If there are no accessible objects or the mouse is moving, this will be false.
*
* @member {Array<*>}
* @private
@@ -151,7 +151,7 @@
/**
* Activating will cause the Accessibility layer to be shown. This is called when a user
- * preses the tab key
+ * preses the tab key.
*
* @private
*/
@@ -177,7 +177,7 @@
/**
* Deactivating will cause the Accessibility layer to be hidden. This is called when a user moves
- * the mouse
+ * the mouse.
*
* @private
*/
@@ -202,7 +202,7 @@
}
/**
- * This recursive function will run throught he scene graph and add any new accessible objects to the DOM layer.
+ * This recursive function will run through the scene graph and add any new accessible objects to the DOM layer.
*
* @private
* @param {PIXI.Container} displayObject - The DisplayObject to check.
@@ -409,7 +409,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseover)
+ * Maps the div focus events to pixi's InteractionManager (mouseover)
*
* @private
* @param {FocusEvent} e - The focus event.
@@ -422,7 +422,7 @@
}
/**
- * Maps the div focus events to pixis InteractionManager (mouseout)
+ * Maps the div focus events to pixi's InteractionManager (mouseout)
*
* @private
* @param {FocusEvent} e - The focusout event.
diff --git a/src/accessibility/index.js b/src/accessibility/index.js
index 568cfb1..0ae108f 100644
--- a/src/accessibility/index.js
+++ b/src/accessibility/index.js
@@ -1,4 +1,9 @@
/**
+ * This namespace contains a renderer plugin for interaction accessibility for end-users
+ * with physical impairments which require screen-renders, keyboard navigation, etc.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.accessibility
*/
export { default as accessibleTarget } from './accessibleTarget';
diff --git a/src/core/Application.js b/src/core/Application.js
index 5340578..2fcc729 100644
--- a/src/core/Application.js
+++ b/src/core/Application.js
@@ -1,6 +1,8 @@
import { autoDetectRenderer } from './autoDetectRenderer';
import Container from './display/Container';
import { shared, Ticker } from './ticker';
+import settings from './settings';
+import { UPDATE_PRIORITY } from './const';
/**
* Convenience class to create a new PIXI application.
@@ -22,28 +24,52 @@
*/
export default class Application
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experience unexplained flickering try setting this to true.
- * @param {boolean} [useSharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker.
+ * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader.
*/
- constructor(width, height, options, noWebGL, useSharedTicker = false)
+ constructor(options, arg2, arg3, arg4, arg5)
{
+ // Support for constructor(width, height, options, noWebGL, useSharedTicker)
+ if (typeof options === 'number')
+ {
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ forceCanvas: !!arg4,
+ sharedTicker: !!arg5,
+ }, arg3);
+ }
+
+ /**
+ * The default options, so we mixin functionality later.
+ * @member {object}
+ * @protected
+ */
+ this._options = options = Object.assign({
+ sharedTicker: false,
+ forceCanvas: false,
+ sharedLoader: false,
+ }, options);
+
/**
* WebGL renderer if available, otherwise CanvasRenderer
* @member {PIXI.WebGLRenderer|PIXI.CanvasRenderer}
*/
- this.renderer = autoDetectRenderer(width, height, options, noWebGL);
+ this.renderer = autoDetectRenderer(options);
/**
* The root display container that's rendered.
@@ -63,7 +89,7 @@
* @member {PIXI.ticker.Ticker}
* @default PIXI.ticker.shared
*/
- this.ticker = useSharedTicker ? shared : new Ticker();
+ this.ticker = options.sharedTicker ? shared : new Ticker();
// Start the rendering
this.start();
@@ -78,7 +104,7 @@
this._ticker = ticker;
if (ticker)
{
- ticker.add(this.render, this);
+ ticker.add(this.render, this, UPDATE_PRIORITY.LOW);
}
}
get ticker() // eslint-disable-line require-jsdoc
@@ -136,13 +162,18 @@
*/
destroy(removeView)
{
- this.stop();
+ const oldTicker = this._ticker;
+
this.ticker = null;
+ oldTicker.destroy();
+
this.stage.destroy();
this.stage = null;
this.renderer.destroy(removeView);
this.renderer = null;
+
+ this._options = null;
}
}
diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js
index 8960425..9ec0364 100644
--- a/src/core/autoDetectRenderer.js
+++ b/src/core/autoDetectRenderer.js
@@ -2,6 +2,7 @@
import CanvasRenderer from './renderers/canvas/CanvasRenderer';
import WebGLRenderer from './renderers/webgl/WebGLRenderer';
+// eslint-disable-next-line valid-jsdoc
/**
* This helper function will automatically detect which renderer you should be using.
* WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by
@@ -9,24 +10,32 @@
*
* @memberof PIXI
* @function autoDetectRenderer
- * @param {number} [width=800] - the width of the renderers view
- * @param {number} [height=600] - the height of the renderers view
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the renderers view
+ * @param {number} [options.height=600] - the height of the renderers view
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment)
* @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you
* need to call toDataUrl on the webgl context
* @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2
- * @param {boolean} [noWebGL=false] - prevents selection of WebGL renderer, even if such is present
+ * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present
* @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer
*/
-export function autoDetectRenderer(width = 800, height = 600, options, noWebGL)
+export function autoDetectRenderer(options, arg1, arg2, arg3)
{
- if (!noWebGL && utils.isWebGLSupported())
+ // Backward-compatible support for noWebGL option
+ let forceCanvas = options && options.forceCanvas;
+
+ if (arg3 !== undefined)
{
- return new WebGLRenderer(width, height, options);
+ forceCanvas = arg3;
}
- return new CanvasRenderer(width, height, options);
+ if (!forceCanvas && utils.isWebGLSupported())
+ {
+ return new WebGLRenderer(options, arg1, arg2);
+ }
+
+ return new CanvasRenderer(options, arg1, arg2);
}
diff --git a/src/core/const.js b/src/core/const.js
index f9235e4..a26d950 100644
--- a/src/core/const.js
+++ b/src/core/const.js
@@ -309,3 +309,27 @@
LINEAR_VERTICAL: 0,
LINEAR_HORIZONTAL: 1,
};
+
+/**
+ * Represents the update priorities used by internal PIXI classes when registered with
+ * the {@link PIXI.ticker.Ticker} object. Higher priority items are updated first and lower
+ * priority items, such as render, should go later.
+ *
+ * @static
+ * @constant
+ * @name UPDATE_PRIORITY
+ * @memberof PIXI
+ * @type {object}
+ * @property {number} INTERACTION=50 Highest priority, used for {@link PIXI.interaction.InteractionManager}
+ * @property {number} HIGH=25 High priority updating, {@link PIXI.VideoBaseTexture} and {@link PIXI.extras.AnimatedSprite}
+ * @property {number} NORMAL=0 Default priority for ticker events, see {@link PIXI.ticker.Ticker#add}.
+ * @property {number} LOW=-25 Low priority used for {@link PIXI.Application} rendering.
+ * @property {number} UTILITY=-50 Lowest priority used for {@link PIXI.prepare.BasePrepare} utility.
+ */
+export const UPDATE_PRIORITY = {
+ INTERACTION: 50,
+ HIGH: 25,
+ NORMAL: 0,
+ LOW: -25,
+ UTILITY: -50,
+};
diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js
index f5d615c..decb42a 100644
--- a/src/core/display/DisplayObject.js
+++ b/src/core/display/DisplayObject.js
@@ -13,7 +13,6 @@
*
* @class
* @extends EventEmitter
- * @mixes PIXI.interaction.interactiveTarget
* @memberof PIXI
*/
export default class DisplayObject extends EventEmitter
@@ -122,6 +121,20 @@
* @readonly
*/
this._destroyed = false;
+
+ /**
+ * Fired when this DisplayObject is added to a Container.
+ *
+ * @event PIXI.DisplayObject#added
+ * @param {PIXI.Container} container - The container added to.
+ */
+
+ /**
+ * Fired when this DisplayObject is removed from a Container.
+ *
+ * @event PIXI.DisplayObject#removed
+ * @param {PIXI.Container} container - The container removed from.
+ */
}
/**
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index be4c4fb..9def67f 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -792,8 +792,8 @@
sprite.worldAlpha = this.worldAlpha * sprite.alpha;
sprite.blendMode = this.blendMode;
- sprite.texture._frame.width = rect.width;
- sprite.texture._frame.height = rect.height;
+ sprite._texture._frame.width = rect.width;
+ sprite._texture._frame.height = rect.height;
sprite.transform.worldTransform = this.transform.worldTransform;
@@ -1071,7 +1071,7 @@
canvasRenderer.render(this, canvasBuffer, true, tempMatrix);
- const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode);
+ const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics');
texture.baseTexture.resolution = resolution;
texture.baseTexture.update();
diff --git a/src/core/index.js b/src/core/index.js
index f03fe15..3265dfb 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -26,6 +26,7 @@
export { default as SpriteRenderer } from './sprites/webgl/SpriteRenderer';
export { default as Text } from './text/Text';
export { default as TextStyle } from './text/TextStyle';
+export { default as TextMetrics } from './text/TextMetrics';
export { default as Graphics } from './graphics/Graphics';
export { default as GraphicsData } from './graphics/GraphicsData';
export { default as GraphicsRenderer } from './graphics/webgl/GraphicsRenderer';
diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js
index a24c775..b9f192d 100644
--- a/src/core/renderers/SystemRenderer.js
+++ b/src/core/renderers/SystemRenderer.js
@@ -19,11 +19,12 @@
*/
export default class SystemRenderer extends EventEmitter
{
+ // eslint-disable-next-line valid-jsdoc
/**
* @param {string} system - The name of the system this renderer is for.
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -37,27 +38,31 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(system, screenWidth, screenHeight, options)
+ constructor(system, options, arg2, arg3)
{
super();
sayHello(system);
- // prepare options
- if (options)
+ // Support for constructor(system, screenWidth, screenHeight, options)
+ if (typeof options === 'number')
{
- for (const i in settings.RENDER_OPTIONS)
- {
- if (typeof options[i] === 'undefined')
- {
- options[i] = settings.RENDER_OPTIONS[i];
- }
- }
+ options = Object.assign({
+ width: options,
+ height: arg2 || settings.RENDER_OPTIONS.height,
+ }, arg3);
}
- else
- {
- options = settings.RENDER_OPTIONS;
- }
+
+ // Add the default render options
+ options = Object.assign({}, settings.RENDER_OPTIONS, options);
+
+ /**
+ * The supplied constructor options.
+ *
+ * @member {Object}
+ * @readOnly
+ */
+ this.options = options;
/**
* The type of the renderer.
@@ -75,7 +80,7 @@
*
* @member {PIXI.Rectangle}
*/
- this.screen = new Rectangle(0, 0, screenWidth || 800, screenHeight || 600);
+ this.screen = new Rectangle(0, 0, options.width, options.height);
/**
* The canvas element that everything is drawn to
@@ -279,6 +284,8 @@
this.blendModes = null;
+ this.options = null;
+
this.preserveDrawingBuffer = false;
this.clearBeforeRender = false;
diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js
index e84e49c..691caf0 100644
--- a/src/core/renderers/canvas/CanvasRenderer.js
+++ b/src/core/renderers/canvas/CanvasRenderer.js
@@ -17,10 +17,11 @@
*/
export default class CanvasRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -34,9 +35,9 @@
* @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering,
* stopping pixel interpolation.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('Canvas', screenWidth, screenHeight, options);
+ super('Canvas', options, arg2, arg3);
this.type = RENDERER_TYPE.CANVAS;
@@ -96,7 +97,19 @@
this.context = null;
this.renderingToScreen = false;
- this.resize(screenWidth, screenHeight);
+ this.resize(this.options.width, this.options.height);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.CanvasRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.CanvasRenderer#prerender
+ */
}
/**
@@ -299,4 +312,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.CanvasRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.CanvasExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.CanvasPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.CanvasRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(CanvasRenderer);
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index 788ed6c..d9a1777 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -28,11 +28,12 @@
*/
export default class WebGLRenderer extends SystemRenderer
{
+ // eslint-disable-next-line valid-jsdoc
/**
*
- * @param {number} [screenWidth=800] - the width of the screen
- * @param {number} [screenHeight=600] - the height of the screen
* @param {object} [options] - The optional renderer parameters
+ * @param {number} [options.width=800] - the width of the screen
+ * @param {number} [options.height=600] - the height of the screen
* @param {HTMLCanvasElement} [options.view] - the canvas to use as a view, optional
* @param {boolean} [options.transparent=false] - If the render view is transparent, default false
* @param {boolean} [options.autoResize=false] - If the render view is automatically resized, default false
@@ -52,11 +53,11 @@
* @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility
* with older / less advanced devices. If you experiance unexplained flickering try setting this to true.
*/
- constructor(screenWidth, screenHeight, options = {})
+ constructor(options, arg2, arg3)
{
- super('WebGL', screenWidth, screenHeight, options);
+ super('WebGL', options, arg2, arg3);
- this.legacy = !!options.legacy;
+ this.legacy = this.options.legacy;
if (this.legacy)
{
@@ -85,10 +86,10 @@
*/
this._contextOptions = {
alpha: this.transparent,
- antialias: options.antialias,
+ antialias: this.options.antialias,
premultipliedAlpha: this.transparent && this.transparent !== 'notMultiplied',
stencil: true,
- preserveDrawingBuffer: options.preserveDrawingBuffer,
+ preserveDrawingBuffer: this.options.preserveDrawingBuffer,
};
this._backgroundColorRgba[3] = this.transparent ? 0 : 1;
@@ -129,13 +130,13 @@
* @member {WebGLRenderingContext}
*/
// initialize the context so it is ready for the managers.
- if (options.context)
+ if (this.options.context)
{
// checks to see if a context is valid..
- validateContext(options.context);
+ validateContext(this.options.context);
}
- this.gl = options.context || glCore.createContext(this.view, this._contextOptions);
+ this.gl = this.options.context || glCore.createContext(this.view, this._contextOptions);
this.CONTEXT_UID = CONTEXT_UID++;
@@ -184,6 +185,25 @@
this._nextTextureLocation = 0;
this.setBlendMode(0);
+
+ /**
+ * Fired after rendering finishes.
+ *
+ * @event PIXI.WebGLRenderer#postrender
+ */
+
+ /**
+ * Fired before rendering starts.
+ *
+ * @event PIXI.WebGLRenderer#prerender
+ */
+
+ /**
+ * Fired when the WebGL context is set.
+ *
+ * @event PIXI.WebGLRenderer#context
+ * @param {WebGLRenderingContext} gl - WebGL context.
+ */
}
/**
@@ -706,4 +726,25 @@
}
}
+/**
+ * Collection of installed plugins. These are included by default in PIXI, but can be excluded
+ * by creating a custom build. Consult the README for more information about creating custom
+ * builds and excluding plugins.
+ * @name PIXI.WebGLRenderer#plugins
+ * @type {object}
+ * @readonly
+ * @property {PIXI.accessibility.AccessibilityManager} accessibility Support tabbing interactive elements.
+ * @property {PIXI.extract.WebGLExtract} extract Extract image data from renderer.
+ * @property {PIXI.interaction.InteractionManager} interaction Handles mouse, touch and pointer events.
+ * @property {PIXI.prepare.WebGLPrepare} prepare Pre-render display objects.
+ */
+
+/**
+ * Adds a plugin to the renderer.
+ *
+ * @method PIXI.WebGLRenderer#registerPlugin
+ * @param {string} pluginName - The name of the plugin.
+ * @param {Function} ctor - The constructor function or class for the plugin.
+ */
+
pluginTarget.mixin(WebGLRenderer);
diff --git a/src/core/renderers/webgl/filters/filterTransforms.js b/src/core/renderers/webgl/filters/filterTransforms.js
index 5fde3d3..851ad69 100644
--- a/src/core/renderers/webgl/filters/filterTransforms.js
+++ b/src/core/renderers/webgl/filters/filterTransforms.js
@@ -1,6 +1,6 @@
import { Matrix } from '../../../math';
-/*
+/**
* Calculates the mapped matrix
* @param filterArea {Rectangle} The filter area
* @param sprite {Sprite} the target sprite
diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js
index 218fca5..e8ce543 100644
--- a/src/core/renderers/webgl/utils/Quad.js
+++ b/src/core/renderers/webgl/utils/Quad.js
@@ -15,7 +15,7 @@
*/
constructor(gl, state)
{
- /*
+ /**
* the current WebGL drawing context
*
* @member {WebGLRenderingContext}
@@ -56,23 +56,31 @@
this.interleaved[(i * 4) + 3] = this.uvs[(i * 2) + 1];
}
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = createIndicesForQuads(1);
- /*
- * @member {glCore.GLBuffer} The vertex buffer
+ /**
+ * The vertex buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.vertexBuffer = glCore.GLBuffer.createVertexBuffer(gl, this.interleaved, gl.STATIC_DRAW);
- /*
- * @member {glCore.GLBuffer} The index buffer
+ /**
+ * The index buffer
+ *
+ * @member {glCore.GLBuffer}
*/
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
- /*
- * @member {glCore.VertexArrayObject} The index buffer
+ /**
+ * The vertex array object
+ *
+ * @member {glCore.VertexArrayObject}
*/
this.vao = new glCore.VertexArrayObject(gl, state);
}
diff --git a/src/core/settings.js b/src/core/settings.js
index f4dd087..d75b058 100644
--- a/src/core/settings.js
+++ b/src/core/settings.js
@@ -2,6 +2,15 @@
import canUploadSameBuffer from './utils/canUploadSameBuffer';
/**
+ * User's customizable globals for overriding the default PIXI settings, such
+ * as a renderer's default resolution, framerate, float percision, etc.
+ * @example
+ * // Use the native window resolution as the default resolution
+ * // will support high-density displays when rendering
+ * PIXI.settings.RESOLUTION = window.devicePixelRatio.
+ *
+ * // Disable interpolation when scaling, will make texture be pixelated
+ * PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
* @namespace PIXI.settings
*/
export default {
@@ -101,6 +110,9 @@
* @property {boolean} clearBeforeRender=true
* @property {boolean} preserveDrawingBuffer=false
* @property {boolean} roundPixels=false
+ * @property {number} width=800
+ * @property {number} height=600
+ * @property {boolean} legacy=false
*/
RENDER_OPTIONS: {
view: null,
@@ -112,6 +124,9 @@
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false,
+ width: 800,
+ height: 600,
+ legacy: false,
},
/**
diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js
index 121bf58..cd574ac 100644
--- a/src/core/sprites/Sprite.js
+++ b/src/core/sprites/Sprite.js
@@ -147,12 +147,12 @@
// so if _width is 0 then width was not set..
if (this._width)
{
- this.scale.x = sign(this.scale.x) * this._width / this.texture.orig.width;
+ this.scale.x = sign(this.scale.x) * this._width / this._texture.orig.width;
}
if (this._height)
{
- this.scale.y = sign(this.scale.y) * this._height / this.texture.orig.height;
+ this.scale.y = sign(this.scale.y) * this._height / this._texture.orig.height;
}
}
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index 27e5e0c..fd9de5d 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -18,7 +18,7 @@
*/
getTintedTexture: (sprite, color) =>
{
- const texture = sprite.texture;
+ const texture = sprite._texture;
color = CanvasTinter.roundColor(color);
diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js
index 90b08c0..9ddc126 100644
--- a/src/core/sprites/webgl/SpriteRenderer.js
+++ b/src/core/sprites/webgl/SpriteRenderer.js
@@ -115,7 +115,7 @@
this.MAX_TEXTURES = checkMaxIfStatmentsInShader(this.MAX_TEXTURES, gl);
}
- const shader = this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
+ this.shader = generateMultiTextureShader(gl, this.MAX_TEXTURES);
// create a couple of buffers
this.indexBuffer = glCore.GLBuffer.createIndexBuffer(gl, this.indices, gl.STATIC_DRAW);
@@ -125,25 +125,27 @@
this.renderer.bindVao(null);
+ const attrs = this.shader.attributes;
+
for (let i = 0; i < this.vaoMax; i++)
{
- this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
-
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[i] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[i] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[i], shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[i].addAttribute(this.vertexBuffers[i], shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[i] = vao;
}
this.vao = this.vaos[0];
@@ -379,23 +381,26 @@
if (this.vaoMax <= this.vertexCount)
{
this.vaoMax++;
- this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+
+ const attrs = this.shader.attributes;
/* eslint-disable max-len */
+ const vertexBuffer = this.vertexBuffers[this.vertexCount] = glCore.GLBuffer.createVertexBuffer(gl, null, gl.STREAM_DRAW);
+ /* eslint-enable max-len */
// build the vao object that will render..
- this.vaos[this.vertexCount] = this.renderer.createVao()
+ const vao = this.renderer.createVao()
.addIndex(this.indexBuffer)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
- .addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
+ .addAttribute(vertexBuffer, attrs.aVertexPosition, gl.FLOAT, false, this.vertByteSize, 0)
+ .addAttribute(vertexBuffer, attrs.aTextureCoord, gl.UNSIGNED_SHORT, true, this.vertByteSize, 2 * 4)
+ .addAttribute(vertexBuffer, attrs.aColor, gl.UNSIGNED_BYTE, true, this.vertByteSize, 3 * 4);
- if (this.shader.attributes.aTextureId)
+ if (attrs.aTextureId)
{
- this.vaos[this.vertexCount].addAttribute(this.vertexBuffers[this.vertexCount], this.shader.attributes.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
+ vao.addAttribute(vertexBuffer, attrs.aTextureId, gl.FLOAT, false, this.vertByteSize, 4 * 4);
}
- /* eslint-enable max-len */
+ this.vaos[this.vertexCount] = vao;
}
this.renderer.bindVao(this.vaos[this.vertexCount]);
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index cec8c32..c11239a 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -6,6 +6,7 @@
import { TEXT_GRADIENT } from '../const';
import settings from '../settings';
import TextStyle from './TextStyle';
+import TextMetrics from './TextMetrics';
import trimCanvas from '../utils/trimCanvas';
const defaultDestroyOptions = {
@@ -42,13 +43,16 @@
canvas.width = 3;
canvas.height = 3;
- const texture = Texture.fromCanvas(canvas);
+ const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text');
texture.orig = new Rectangle();
texture.trim = new Rectangle();
super(texture);
+ // base texture is already automatically added to the cache, now adding the actual texture
+ Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]);
+
/**
* The canvas element that everything is drawn to
*
@@ -128,50 +132,18 @@
return;
}
- this._font = Text.getFontStyle(style);
+ this._font = this._style.toFontString();
- this.context.font = this._font;
-
- // word wrap
- // preserve original text
- const outputText = style.wordWrap ? this.wordWrap(this._text) : this._text;
-
- // split text into lines
- const lines = outputText.split(/(?:\r\n|\r|\n)/);
-
- // calculate text width
- const lineWidths = new Array(lines.length);
- let maxLineWidth = 0;
- const fontProperties = Text.calculateFontProperties(this._font);
-
- for (let i = 0; i < lines.length; i++)
- {
- const lineWidth = this.context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
-
- lineWidths[i] = lineWidth;
- maxLineWidth = Math.max(maxLineWidth, lineWidth);
- }
-
- let width = maxLineWidth + style.strokeThickness;
-
- if (style.dropShadow)
- {
- width += style.dropShadowDistance;
- }
+ const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
+ const width = measured.width;
+ const height = measured.height;
+ const lines = measured.lines;
+ const lineHeight = measured.lineHeight;
+ const lineWidths = measured.lineWidths;
+ const maxLineWidth = measured.maxLineWidth;
+ const fontProperties = measured.fontProperties;
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
-
- // calculate text height
- const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
-
- let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
- + ((lines.length - 1) * lineHeight);
-
- if (style.dropShadow)
- {
- height += style.dropShadowDistance;
- }
-
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
this.context.scale(this.resolution, this.resolution);
@@ -264,12 +236,21 @@
if (style.stroke && style.strokeThickness)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding, true);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding,
+ true
+ );
}
if (style.fill)
{
- this.drawLetterSpacing(lines[i], linePositionX + style.padding, linePositionY + style.padding);
+ this.drawLetterSpacing(
+ lines[i],
+ linePositionX + style.padding,
+ linePositionY + style.padding
+ );
}
}
@@ -344,6 +325,7 @@
const texture = this._texture;
const style = this._style;
+ const padding = style.trim ? 0 : style.padding;
texture.baseTexture.hasLoaded = true;
texture.baseTexture.resolution = this.resolution;
@@ -355,11 +337,11 @@
texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
- texture.trim.x = -style.padding;
- texture.trim.y = -style.padding;
+ texture.trim.x = -padding;
+ texture.trim.y = -padding;
- texture.orig.width = texture._frame.width - (style.padding * 2);
- texture.orig.height = texture._frame.height - (style.padding * 2);
+ texture.orig.width = texture._frame.width - (padding * 2);
+ texture.orig.height = texture._frame.height - (padding * 2);
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
@@ -407,90 +389,6 @@
}
/**
- * Applies newlines to a string to have it optimally fit into the horizontal
- * bounds set by the Text object's wordWrapWidth property.
- *
- * @private
- * @param {string} text - String to apply word wrapping to
- * @return {string} New string with new lines applied where required
- */
- wordWrap(text)
- {
- // Greedy wrapping algorithm that will wrap words as the line grows longer
- // than its horizontal bounds.
- let result = '';
- const style = this._style;
- const lines = text.split('\n');
- const wordWrapWidth = style.wordWrapWidth;
-
- for (let i = 0; i < lines.length; i++)
- {
- let spaceLeft = wordWrapWidth;
- const words = lines[i].split(' ');
-
- for (let j = 0; j < words.length; j++)
- {
- const wordWidth = this.context.measureText(words[j]).width;
-
- if (style.breakWords && wordWidth > wordWrapWidth)
- {
- // Word should be split in the middle
- const characters = words[j].split('');
-
- for (let c = 0; c < characters.length; c++)
- {
- const characterWidth = this.context.measureText(characters[c]).width;
-
- if (characterWidth > spaceLeft)
- {
- result += `\n${characters[c]}`;
- spaceLeft = wordWrapWidth - characterWidth;
- }
- else
- {
- if (c === 0)
- {
- result += ' ';
- }
-
- result += characters[c];
- spaceLeft -= characterWidth;
- }
- }
- }
- else
- {
- const wordWidthWithSpace = wordWidth + this.context.measureText(' ').width;
-
- if (j === 0 || wordWidthWithSpace > spaceLeft)
- {
- // Skip printing the newline if it's the first word of the line that is
- // greater than the word wrap width.
- if (j > 0)
- {
- result += '\n';
- }
- result += words[j];
- spaceLeft = wordWrapWidth - wordWidth;
- }
- else
- {
- spaceLeft -= wordWidthWithSpace;
- result += ` ${words[j]}`;
- }
- }
- }
-
- if (i < lines.length - 1)
- {
- result += '\n';
- }
- }
-
- return result;
- }
-
- /**
* Gets the local bounds of the text object.
*
* @param {Rectangle} rect - The output rectangle.
@@ -755,157 +653,4 @@
this._text = text;
this.dirty = true;
}
-
- /**
- * Generates a font style string to use for Text.calculateFontProperties(). Takes the same parameter
- * as Text.style.
- *
- * @static
- * @param {object|TextStyle} style - String representing the style of the font
- * @return {string} Font style string, for passing to Text.calculateFontProperties()
- */
- static getFontStyle(style)
- {
- style = style || {};
-
- if (!(style instanceof TextStyle))
- {
- style = new TextStyle(style);
- }
-
- // build canvas api font setting from individual components. Convert a numeric style.fontSize to px
- const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize;
-
- // Clean-up fontFamily property by quoting each font name
- // this will support font names with spaces
- let fontFamilies = style.fontFamily;
-
- if (!Array.isArray(style.fontFamily))
- {
- fontFamilies = style.fontFamily.split(',');
- }
-
- for (let i = fontFamilies.length - 1; i >= 0; i--)
- {
- // Trim any extra white-space
- let fontFamily = fontFamilies[i].trim();
-
- // Check if font already contains strings
- if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
- {
- fontFamily = `"${fontFamily}"`;
- }
- fontFamilies[i] = fontFamily;
- }
-
- return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
- }
-
- /**
- * Calculates the ascent, descent and fontSize of a given fontStyle
- *
- * @static
- * @param {string} fontStyle - String representing the style of the font
- * @return {Object} Font properties object
- */
- static calculateFontProperties(fontStyle)
- {
- // as this method is used for preparing assets, don't recalculate things if we don't need to
- if (Text.fontPropertiesCache[fontStyle])
- {
- return Text.fontPropertiesCache[fontStyle];
- }
-
- const properties = {};
-
- const canvas = Text.fontPropertiesCanvas;
- const context = Text.fontPropertiesContext;
-
- context.font = fontStyle;
-
- const width = Math.ceil(context.measureText('|MÉq').width);
- let baseline = Math.ceil(context.measureText('M').width);
- const height = 2 * baseline;
-
- baseline = baseline * 1.4 | 0;
-
- canvas.width = width;
- canvas.height = height;
-
- context.fillStyle = '#f00';
- context.fillRect(0, 0, width, height);
-
- context.font = fontStyle;
-
- context.textBaseline = 'alphabetic';
- context.fillStyle = '#000';
- context.fillText('|MÉq', 0, baseline);
-
- const imagedata = context.getImageData(0, 0, width, height).data;
- const pixels = imagedata.length;
- const line = width * 4;
-
- let i = 0;
- let idx = 0;
- let stop = false;
-
- // ascent. scan from top to bottom until we find a non red pixel
- for (i = 0; i < baseline; ++i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
- if (!stop)
- {
- idx += line;
- }
- else
- {
- break;
- }
- }
-
- properties.ascent = baseline - i;
-
- idx = pixels - line;
- stop = false;
-
- // descent. scan from bottom to top until we find a non red pixel
- for (i = height; i > baseline; --i)
- {
- for (let j = 0; j < line; j += 4)
- {
- if (imagedata[idx + j] !== 255)
- {
- stop = true;
- break;
- }
- }
-
- if (!stop)
- {
- idx -= line;
- }
- else
- {
- break;
- }
- }
-
- properties.descent = i - baseline;
- properties.fontSize = properties.ascent + properties.descent;
-
- Text.fontPropertiesCache[fontStyle] = properties;
-
- return properties;
- }
}
-
-Text.fontPropertiesCache = {};
-Text.fontPropertiesCanvas = document.createElement('canvas');
-Text.fontPropertiesContext = Text.fontPropertiesCanvas.getContext('2d');
diff --git a/src/core/text/TextMetrics.js b/src/core/text/TextMetrics.js
new file mode 100644
index 0000000..27bd145
--- /dev/null
+++ b/src/core/text/TextMetrics.js
@@ -0,0 +1,327 @@
+/**
+ * The TextMetrics object represents the measurement of a block of text with a specified style.
+ *
+ * @class
+ * @memberOf PIXI
+ */
+export default class TextMetrics
+{
+ /**
+ * @param {string} text - the text that was measured
+ * @param {PIXI.TextStyle} style - the style that was measured
+ * @param {number} width - the measured width of the text
+ * @param {number} height - the measured height of the text
+ * @param {array} lines - an array of the lines of text broken by new lines and wrapping if specified in style
+ * @param {array} lineWidths - an array of the line widths for each line matched to `lines`
+ * @param {number} lineHeight - the measured line height for this style
+ * @param {number} maxLineWidth - the maximum line width for all measured lines
+ * @param {Object} fontProperties - the font properties object from TextMetrics.measureFont
+ */
+ constructor(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties)
+ {
+ this.text = text;
+ this.style = style;
+ this.width = width;
+ this.height = height;
+ this.lines = lines;
+ this.lineWidths = lineWidths;
+ this.lineHeight = lineHeight;
+ this.maxLineWidth = maxLineWidth;
+ this.fontProperties = fontProperties;
+ }
+
+ /**
+ * Measures the supplied string of text and returns a Rectangle.
+ *
+ * @param {string} text - the text to measure.
+ * @param {PIXI.TextStyle} style - the text style to use for measuring
+ * @param {boolean} [wordWrap] - optional override for if word-wrap should be applied to the text.
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {PIXI.TextMetrics} measured width and height of the text.
+ */
+ static measureText(text, style, wordWrap, canvas = TextMetrics._canvas)
+ {
+ wordWrap = wordWrap || style.wordWrap;
+ const font = style.toFontString();
+ const fontProperties = TextMetrics.measureFont(font);
+ const context = canvas.getContext('2d');
+
+ context.font = font;
+
+ const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
+ const lines = outputText.split(/(?:\r\n|\r|\n)/);
+ const lineWidths = new Array(lines.length);
+ let maxLineWidth = 0;
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ const lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
+
+ lineWidths[i] = lineWidth;
+ maxLineWidth = Math.max(maxLineWidth, lineWidth);
+ }
+ let width = maxLineWidth + style.strokeThickness;
+
+ if (style.dropShadow)
+ {
+ width += style.dropShadowDistance;
+ }
+
+ const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
+ let height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
+ + ((lines.length - 1) * lineHeight);
+
+ if (style.dropShadow)
+ {
+ height += style.dropShadowDistance;
+ }
+
+ return new TextMetrics(
+ text,
+ style,
+ width,
+ height,
+ lines,
+ lineWidths,
+ lineHeight,
+ maxLineWidth,
+ fontProperties
+ );
+ }
+
+ /**
+ * Applies newlines to a string to have it optimally fit into the horizontal
+ * bounds set by the Text object's wordWrapWidth property.
+ *
+ * @private
+ * @param {string} text - String to apply word wrapping to
+ * @param {PIXI.TextStyle} style - the style to use when wrapping
+ * @param {HTMLCanvasElement} [canvas] - optional specification of the canvas to use for measuring.
+ * @return {string} New string with new lines applied where required
+ */
+ static wordWrap(text, style, canvas = TextMetrics._canvas)
+ {
+ const context = canvas.getContext('2d');
+
+ // Greedy wrapping algorithm that will wrap words as the line grows longer
+ // than its horizontal bounds.
+ let result = '';
+ const lines = text.split('\n');
+ const wordWrapWidth = style.wordWrapWidth;
+ const characterCache = {};
+
+ for (let i = 0; i < lines.length; i++)
+ {
+ let spaceLeft = wordWrapWidth;
+ const words = lines[i].split(' ');
+
+ for (let j = 0; j < words.length; j++)
+ {
+ const wordWidth = context.measureText(words[j]).width;
+
+ if (style.breakWords && wordWidth > wordWrapWidth)
+ {
+ // Word should be split in the middle
+ const characters = words[j].split('');
+
+ for (let c = 0; c < characters.length; c++)
+ {
+ const character = characters[c];
+ let characterWidth = characterCache[character];
+
+ if (characterWidth === undefined)
+ {
+ characterWidth = context.measureText(character).width;
+ characterCache[character] = characterWidth;
+ }
+
+ if (characterWidth > spaceLeft)
+ {
+ result += `\n${character}`;
+ spaceLeft = wordWrapWidth - characterWidth;
+ }
+ else
+ {
+ if (c === 0)
+ {
+ result += ' ';
+ }
+
+ result += character;
+ spaceLeft -= characterWidth;
+ }
+ }
+ }
+ else
+ {
+ const wordWidthWithSpace = wordWidth + context.measureText(' ').width;
+
+ if (j === 0 || wordWidthWithSpace > spaceLeft)
+ {
+ // Skip printing the newline if it's the first word of the line that is
+ // greater than the word wrap width.
+ if (j > 0)
+ {
+ result += '\n';
+ }
+ result += words[j];
+ spaceLeft = wordWrapWidth - wordWidth;
+ }
+ else
+ {
+ spaceLeft -= wordWidthWithSpace;
+ result += ` ${words[j]}`;
+ }
+ }
+ }
+
+ if (i < lines.length - 1)
+ {
+ result += '\n';
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Calculates the ascent, descent and fontSize of a given font-style
+ *
+ * @static
+ * @param {string} font - String representing the style of the font
+ * @return {PIXI.TextMetrics~FontMetrics} Font properties object
+ */
+ static measureFont(font)
+ {
+ // as this method is used for preparing assets, don't recalculate things if we don't need to
+ if (TextMetrics._fonts[font])
+ {
+ return TextMetrics._fonts[font];
+ }
+
+ const properties = {};
+
+ const canvas = TextMetrics._canvas;
+ const context = TextMetrics._context;
+
+ context.font = font;
+
+ const width = Math.ceil(context.measureText('|MÉq').width);
+ let baseline = Math.ceil(context.measureText('M').width);
+ const height = 2 * baseline;
+
+ baseline = baseline * 1.4 | 0;
+
+ canvas.width = width;
+ canvas.height = height;
+
+ context.fillStyle = '#f00';
+ context.fillRect(0, 0, width, height);
+
+ context.font = font;
+
+ context.textBaseline = 'alphabetic';
+ context.fillStyle = '#000';
+ context.fillText('|MÉq', 0, baseline);
+
+ const imagedata = context.getImageData(0, 0, width, height).data;
+ const pixels = imagedata.length;
+ const line = width * 4;
+
+ let i = 0;
+ let idx = 0;
+ let stop = false;
+
+ // ascent. scan from top to bottom until we find a non red pixel
+ for (i = 0; i < baseline; ++i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+ if (!stop)
+ {
+ idx += line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.ascent = baseline - i;
+
+ idx = pixels - line;
+ stop = false;
+
+ // descent. scan from bottom to top until we find a non red pixel
+ for (i = height; i > baseline; --i)
+ {
+ for (let j = 0; j < line; j += 4)
+ {
+ if (imagedata[idx + j] !== 255)
+ {
+ stop = true;
+ break;
+ }
+ }
+
+ if (!stop)
+ {
+ idx -= line;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ properties.descent = i - baseline;
+ properties.fontSize = properties.ascent + properties.descent;
+
+ TextMetrics._fonts[font] = properties;
+
+ return properties;
+ }
+}
+
+/**
+ * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
+ * @class FontMetrics
+ * @memberof PIXI.TextMetrics~
+ * @property {number} ascent - The ascent distance
+ * @property {number} descent - The descent distance
+ * @property {number} fontSize - Font size from ascent to descent
+ */
+
+const canvas = document.createElement('canvas');
+
+canvas.width = canvas.height = 10;
+
+/**
+ * Cached canvas element for measuring text
+ * @memberof PIXI.TextMetrics
+ * @type {HTMLCanvasElement}
+ * @private
+ */
+TextMetrics._canvas = canvas;
+
+/**
+ * Cache for context to use.
+ * @memberof PIXI.TextMetrics
+ * @type {CanvasRenderingContext2D}
+ * @private
+ */
+TextMetrics._context = canvas.getContext('2d');
+
+/**
+ * Cache of PIXI.TextMetrics~FontMetrics objects.
+ * @memberof PIXI.TextMetrics
+ * @type {Object}
+ * @private
+ */
+TextMetrics._fonts = {};
diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js
index 890950b..4d66953 100644
--- a/src/core/text/TextStyle.js
+++ b/src/core/text/TextStyle.js
@@ -473,6 +473,41 @@
this.styleID++;
}
}
+
+ /**
+ * Generates a font style string to use for `TextMetrics.measureFont()`.
+ *
+ * @return {string} Font style string, for passing to `TextMetrics.measureFont()`
+ */
+ toFontString()
+ {
+ // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
+ const fontSizeString = (typeof this.fontSize === 'number') ? `${this.fontSize}px` : this.fontSize;
+
+ // Clean-up fontFamily property by quoting each font name
+ // this will support font names with spaces
+ let fontFamilies = this.fontFamily;
+
+ if (!Array.isArray(this.fontFamily))
+ {
+ fontFamilies = this.fontFamily.split(',');
+ }
+
+ for (let i = fontFamilies.length - 1; i >= 0; i--)
+ {
+ // Trim any extra white-space
+ let fontFamily = fontFamilies[i].trim();
+
+ // Check if font already contains strings
+ if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily))
+ {
+ fontFamily = `"${fontFamily}"`;
+ }
+ fontFamilies[i] = fontFamily;
+ }
+
+ return `${this.fontStyle} ${this.fontVariant} ${this.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`;
+ }
}
/**
diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js
index bbee141..094ad8b 100644
--- a/src/core/textures/BaseTexture.js
+++ b/src/core/textures/BaseTexture.js
@@ -196,12 +196,6 @@
this._enabled = 0;
this._virtalBoundId = -1;
- // if no source passed don't try to load
- if (source)
- {
- this.loadSource(source);
- }
-
/**
* If the object has been destroyed via destroy(). If true, it should not be used.
*
@@ -212,26 +206,57 @@
this._destroyed = false;
/**
+ * The ids under which this BaseTexture has been added to the base texture cache. This is
+ * automatically set as long as BaseTexture.addToCache is used, but may not be set if a
+ * BaseTexture is added directly to the BaseTextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
+
+ // if no source passed don't try to load
+ if (source)
+ {
+ this.loadSource(source);
+ }
+
+ /**
* Fired when a not-immediately-available source finishes loading.
*
* @protected
- * @event loaded
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#loaded
+ * @param {PIXI.BaseTexture} baseTexture - Resource loaded.
*/
/**
* Fired when a not-immediately-available source fails to load.
*
* @protected
- * @event error
- * @memberof PIXI.BaseTexture#
+ * @event PIXI.BaseTexture#error
+ * @param {PIXI.BaseTexture} baseTexture - Resource errored.
+ */
+
+ /**
+ * Fired when BaseTexture is updated.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#update
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being updated.
+ */
+
+ /**
+ * Fired when BaseTexture is destroyed.
+ *
+ * @protected
+ * @event PIXI.BaseTexture#dispose
+ * @param {PIXI.BaseTexture} baseTexture - Instance of texture being destroyed.
*/
}
/**
* Updates the texture on all the webgl renderers, this also assumes the src has changed.
*
- * @fires update
+ * @fires PIXI.BaseTexture#update
*/
update()
{
@@ -524,7 +549,7 @@
*
* @param {string} svgString SVG source as string
*
- * @fires loaded
+ * @fires PIXI.BaseTexture#loaded
*/
_loadSvgSourceUsingString(svgString)
{
@@ -561,7 +586,7 @@
this.source = canvas;
// Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`)
- BaseTextureCache[canvas._pixiId] = this;
+ BaseTexture.addToCache(this, canvas._pixiId);
this.isLoading = false;
this._sourceLoaded();
@@ -588,7 +613,6 @@
{
if (this.imageUrl)
{
- delete BaseTextureCache[this.imageUrl];
delete TextureCache[this.imageUrl];
this.imageUrl = null;
@@ -598,16 +622,14 @@
this.source.src = '';
}
}
- // An svg source has both `imageUrl` and `__pixiId`, so no `else if` here
- if (this.source && this.source._pixiId)
- {
- delete BaseTextureCache[this.source._pixiId];
- }
this.source = null;
this.dispose();
+ BaseTexture.removeFromCache(this);
+ this.textureCacheIds = null;
+
this._destroyed = true;
}
@@ -616,6 +638,7 @@
* This means you can still use the texture later which will upload it to GPU
* memory again.
*
+ * @fires PIXI.BaseTexture#dispose
*/
dispose()
{
@@ -674,7 +697,7 @@
image.src = imageUrl; // Setting this triggers load
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -686,13 +709,14 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.BaseTexture} The new base texture.
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
if (!canvas._pixiId)
{
- canvas._pixiId = `canvas_${uid()}`;
+ canvas._pixiId = `${origin}_${uid()}`;
}
let baseTexture = BaseTextureCache[canvas._pixiId];
@@ -700,7 +724,7 @@
if (!baseTexture)
{
baseTexture = new BaseTexture(canvas, scaleMode);
- BaseTextureCache[canvas._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, canvas._pixiId);
}
return baseTexture;
@@ -740,7 +764,7 @@
// if there is an @2x at the end of the url we are going to assume its a highres image
baseTexture.resolution = getResolutionOfUrl(imageUrl);
- BaseTextureCache[imageUrl] = baseTexture;
+ BaseTexture.addToCache(baseTexture, imageUrl);
}
return baseTexture;
@@ -753,4 +777,75 @@
// lets assume its a base texture!
return source;
}
+
+ /**
+ * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object.
+ *
+ * @static
+ * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache.
+ * @param {string} id - The id that the BaseTexture will be stored against.
+ */
+ static addToCache(baseTexture, id)
+ {
+ if (id)
+ {
+ if (baseTexture.textureCacheIds.indexOf(id) === -1)
+ {
+ baseTexture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (BaseTextureCache[id])
+ {
+ console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ BaseTextureCache[id] = baseTexture;
+ }
+ }
+
+ /**
+ * Remove a BaseTexture from the global BaseTextureCache.
+ *
+ * @static
+ * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself.
+ * @return {PIXI.BaseTexture|null} The BaseTexture that was removed.
+ */
+ static removeFromCache(baseTexture)
+ {
+ if (typeof baseTexture === 'string')
+ {
+ const baseTextureFromCache = BaseTextureCache[baseTexture];
+
+ if (baseTextureFromCache)
+ {
+ const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture);
+
+ if (index > -1)
+ {
+ baseTextureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete BaseTextureCache[baseTexture];
+
+ return baseTextureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < baseTexture.textureCacheIds.length; ++i)
+ {
+ delete BaseTextureCache[baseTexture.textureCacheIds[i]];
+ }
+
+ baseTexture.textureCacheIds.length = 0;
+
+ return baseTexture;
+ }
+
+ return null;
+ }
}
diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js
index d469c62..85abccf 100644
--- a/src/core/textures/Spritesheet.js
+++ b/src/core/textures/Spritesheet.js
@@ -1,5 +1,5 @@
import { Rectangle, Texture } from '../';
-import { getResolutionOfUrl, TextureCache } from '../utils';
+import { getResolutionOfUrl } from '../utils';
/**
* Utility class for maintaining reference to a collection
@@ -207,7 +207,7 @@
);
// lets also add the frame to pixi's global cache for fromFrame and fromImage functions
- TextureCache[i] = this.textures[i];
+ Texture.addToCache(this.textures[i], i);
}
frameIndex++;
diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js
index 133a59b..6c4d324 100644
--- a/src/core/textures/Texture.js
+++ b/src/core/textures/Texture.js
@@ -3,7 +3,8 @@
import TextureUvs from './TextureUvs';
import EventEmitter from 'eventemitter3';
import { Rectangle } from '../math';
-import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils';
+import { TextureCache, getResolutionOfUrl } from '../utils';
+import settings from '../settings';
/**
* A texture stores the information that represents an image or part of an image. It cannot be added
@@ -144,9 +145,9 @@
/**
* Fired when the texture is updated. This happens if the frame or the baseTexture is updated.
*
- * @event update
- * @memberof PIXI.Texture#
+ * @event PIXI.Texture#update
* @protected
+ * @param {PIXI.Texture} texture - Instance of texture being updated.
*/
this._updateID = 0;
@@ -156,6 +157,15 @@
* @type {Object}
*/
this.transform = null;
+
+ /**
+ * The ids under which this Texture has been added to the texture cache. This is
+ * automatically set as long as Texture.addToCache is used, but may not be set if a
+ * Texture is added directly to the TextureCache array.
+ *
+ * @member {string[]}
+ */
+ this.textureCacheIds = [];
}
/**
@@ -222,7 +232,7 @@
// this only needs to be removed if the base texture is actually destroyed too..
if (TextureCache[this.baseTexture.imageUrl])
{
- delete TextureCache[this.baseTexture.imageUrl];
+ Texture.removeFromCache(this.baseTexture.imageUrl);
}
this.baseTexture.destroy();
@@ -241,8 +251,8 @@
this.valid = false;
- this.off('dispose', this.dispose, this);
- this.off('update', this.update, this);
+ Texture.removeFromCache(this);
+ this.textureCacheIds = null;
}
/**
@@ -290,7 +300,7 @@
if (!texture)
{
texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale));
- TextureCache[imageUrl] = texture;
+ Texture.addToCache(texture, imageUrl);
}
return texture;
@@ -322,11 +332,12 @@
* @static
* @param {HTMLCanvasElement} canvas - The canvas element source of the texture
* @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values
+ * @param {string} [origin='canvas'] - A string origin of who created the base texture
* @return {PIXI.Texture} The newly created texture
*/
- static fromCanvas(canvas, scaleMode)
+ static fromCanvas(canvas, scaleMode, origin = 'canvas')
{
- return new Texture(BaseTexture.fromCanvas(canvas, scaleMode));
+ return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin));
}
/**
@@ -398,7 +409,7 @@
}
else if (source instanceof HTMLCanvasElement)
{
- return Texture.fromCanvas(source);
+ return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement');
}
else if (source instanceof HTMLVideoElement)
{
@@ -437,46 +448,88 @@
}
// lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions
- BaseTextureCache[name] = baseTexture;
- TextureCache[name] = texture;
+ BaseTexture.addToCache(texture.baseTexture, name);
+ Texture.addToCache(texture, name);
// also add references by url if they are different.
if (name !== imageUrl)
{
- BaseTextureCache[imageUrl] = baseTexture;
- TextureCache[imageUrl] = texture;
+ BaseTexture.addToCache(texture.baseTexture, imageUrl);
+ Texture.addToCache(texture, imageUrl);
}
return texture;
}
/**
- * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object.
+ * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object.
*
* @static
* @param {PIXI.Texture} texture - The Texture to add to the cache.
- * @param {string} id - The id that the texture will be stored against.
+ * @param {string} id - The id that the Texture will be stored against.
*/
- static addTextureToCache(texture, id)
+ static addToCache(texture, id)
{
- TextureCache[id] = texture;
+ if (id)
+ {
+ if (texture.textureCacheIds.indexOf(id) === -1)
+ {
+ texture.textureCacheIds.push(id);
+ }
+
+ // @if DEBUG
+ /* eslint-disable no-console */
+ if (TextureCache[id])
+ {
+ console.warn(`Texture added to the cache with an id [${id}] that already had an entry`);
+ }
+ /* eslint-enable no-console */
+ // @endif
+
+ TextureCache[id] = texture;
+ }
}
/**
- * Remove a texture from the global TextureCache.
+ * Remove a Texture from the global TextureCache.
*
* @static
- * @param {string} id - The id of the texture to be removed
- * @return {PIXI.Texture} The texture that was removed
+ * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself
+ * @return {PIXI.Texture|null} The Texture that was removed
*/
- static removeTextureFromCache(id)
+ static removeFromCache(texture)
{
- const texture = TextureCache[id];
+ if (typeof texture === 'string')
+ {
+ const textureFromCache = TextureCache[texture];
- delete TextureCache[id];
- delete BaseTextureCache[id];
+ if (textureFromCache)
+ {
+ const index = textureFromCache.textureCacheIds.indexOf(texture);
- return texture;
+ if (index > -1)
+ {
+ textureFromCache.textureCacheIds.splice(index, 1);
+ }
+
+ delete TextureCache[texture];
+
+ return textureFromCache;
+ }
+ }
+ else
+ {
+ for (let i = 0; i < texture.textureCacheIds.length; ++i)
+ {
+ delete TextureCache[texture.textureCacheIds[i]];
+ }
+
+ texture.textureCacheIds.length = 0;
+
+ return texture;
+ }
+
+ return null;
}
/**
diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js
index 59eb9fd..65b74b9 100644
--- a/src/core/textures/VideoBaseTexture.js
+++ b/src/core/textures/VideoBaseTexture.js
@@ -1,6 +1,7 @@
import BaseTexture from './BaseTexture';
import { uid, BaseTextureCache } from '../utils';
-import * as ticker from '../ticker';
+import { shared } from '../ticker';
+import { UPDATE_PRIORITY } from '../const';
/**
* A texture of a [playing] Video.
@@ -125,7 +126,7 @@
if (!this._isAutoUpdating && this.autoUpdate)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
@@ -139,7 +140,7 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
}
@@ -187,12 +188,12 @@
{
if (this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
}
if (this.source && this.source._pixiId)
{
- delete BaseTextureCache[this.source._pixiId];
+ BaseTexture.removeFromCache(this.source._pixiId);
delete this.source._pixiId;
}
@@ -219,7 +220,7 @@
if (!baseTexture)
{
baseTexture = new VideoBaseTexture(video, scaleMode);
- BaseTextureCache[video._pixiId] = baseTexture;
+ BaseTexture.addToCache(baseTexture, video._pixiId);
}
return baseTexture;
@@ -281,12 +282,12 @@
if (!this._autoUpdate && this._isAutoUpdating)
{
- ticker.shared.remove(this.update, this);
+ shared.remove(this.update, this);
this._isAutoUpdating = false;
}
else if (this._autoUpdate && !this._isAutoUpdating)
{
- ticker.shared.add(this.update, this);
+ shared.add(this.update, this, UPDATE_PRIORITY.HIGH);
this._isAutoUpdating = true;
}
}
diff --git a/src/core/ticker/Ticker.js b/src/core/ticker/Ticker.js
index 17a1517..40df138 100644
--- a/src/core/ticker/Ticker.js
+++ b/src/core/ticker/Ticker.js
@@ -1,12 +1,10 @@
import settings from '../settings';
-import EventEmitter from 'eventemitter3';
-
-// Internal event used by composed emitter
-const TICK = 'tick';
+import { UPDATE_PRIORITY } from '../const';
+import TickerListener from './TickerListener';
/**
* A Ticker class that runs an update loop that other objects listen to.
- * This class is composed around an EventEmitter object to add listeners
+ * This class is composed around listeners
* meant for execution on the next requested animation frame.
* Animation frames are requested only when necessary,
* e.g. When the ticker is started and the emitter has listeners.
@@ -22,10 +20,11 @@
constructor()
{
/**
- * Internal emitter used to fire 'tick' event
+ * The first listener. All new listeners added are chained on this.
* @private
+ * @type {TickerListener}
*/
- this._emitter = new EventEmitter();
+ this._head = new TickerListener(null, null, Infinity);
/**
* Internal current frame request ID
@@ -131,7 +130,7 @@
// Invoke listeners now
this.update(time);
// Listener side effects may have modified ticker state.
- if (this.started && this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this.started && this._requestId === null && this._head.next)
{
this._requestId = requestAnimationFrame(this._tick);
}
@@ -148,7 +147,7 @@
*/
_requestIfNeeded()
{
- if (this._requestId === null && this._emitter.listeners(TICK, true))
+ if (this._requestId === null && this._head.next)
{
// ensure callbacks get correct delta
this.lastTime = performance.now();
@@ -193,35 +192,72 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#on} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Register a handler for tick events. Calls continuously unless
+ * it is removed or the ticker is stopped.
*
* @param {Function} fn - The listener function to be added for updates
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- add(fn, context)
+ add(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.on(TICK, fn, context);
-
- this._startIfPossible();
-
- return this;
+ return this._addListener(new TickerListener(fn, context, priority));
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#once} internally for the
- * internal 'tick' event. It checks if the emitter has listeners,
- * and if so it requests a new animation frame at this point.
+ * Add a handler for the tick event which is only execute once.
*
* @param {Function} fn - The listener function to be added for one update
* @param {Function} [context] - The listener context
+ * @param {number} [priority=PIXI.UPDATE_PRIORITY.NORMAL] - The priority for emitting
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
- addOnce(fn, context)
+ addOnce(fn, context, priority = UPDATE_PRIORITY.NORMAL)
{
- this._emitter.once(TICK, fn, context);
+ return this._addListener(new TickerListener(fn, context, priority, true));
+ }
+
+ /**
+ * Internally adds the event handler so that it can be sorted by priority.
+ * Priority allows certain handler (user, AnimatedSprite, Interaction) to be run
+ * before the rendering.
+ *
+ * @private
+ * @param {TickerListener} listener - Current listener being added.
+ * @returns {PIXI.ticker.Ticker} This instance of a ticker
+ */
+ _addListener(listener)
+ {
+ // For attaching to head
+ let current = this._head.next;
+ let previous = this._head;
+
+ // Add the first item
+ if (!current)
+ {
+ listener.connect(previous);
+ }
+ else
+ {
+ // Go from highest to lowest priority
+ while (current)
+ {
+ if (listener.priority >= current.priority)
+ {
+ listener.connect(previous);
+ break;
+ }
+ previous = current;
+ current = current.next;
+ }
+
+ // Not yet connected
+ if (!listener.previous)
+ {
+ listener.connect(previous);
+ }
+ }
this._startIfPossible();
@@ -229,19 +265,33 @@
}
/**
- * Calls {@link module:eventemitter3.EventEmitter#off} internally for 'tick' event.
- * It checks if the emitter has listeners for 'tick' event.
- * If it does, then it cancels the animation frame.
+ * Removes any handlers matching the function and context parameters.
+ * If no handlers are left after removing, then it cancels the animation frame.
*
- * @param {Function} [fn] - The listener function to be removed
+ * @param {Function} fn - The listener function to be removed
* @param {Function} [context] - The listener context to be removed
* @returns {PIXI.ticker.Ticker} This instance of a ticker
*/
remove(fn, context)
{
- this._emitter.off(TICK, fn, context);
+ let listener = this._head.next;
- if (!this._emitter.listeners(TICK, true))
+ while (listener)
+ {
+ // We found a match, lets remove it
+ // no break to delete all possible matches
+ // incase a listener was added 2+ times
+ if (listener.match(fn, context))
+ {
+ listener = listener.destroy();
+ }
+ else
+ {
+ listener = listener.next;
+ }
+ }
+
+ if (!this._head.next)
{
this._cancelIfNeeded();
}
@@ -276,6 +326,25 @@
}
/**
+ * Destroy the ticker and don't use after this. Calling
+ * this method removes all references to internal events.
+ */
+ destroy()
+ {
+ this.stop();
+
+ let listener = this._head.next;
+
+ while (listener)
+ {
+ listener = listener.destroy(true);
+ }
+
+ this._head.destroy();
+ this._head = null;
+ }
+
+ /**
* Triggers an update. An update entails setting the
* current {@link PIXI.ticker.Ticker#elapsedMS},
* the current {@link PIXI.ticker.Ticker#deltaTime},
@@ -320,8 +389,22 @@
this.deltaTime = elapsedMS * settings.TARGET_FPMS * this.speed;
+ // Cache a local reference, in-case ticker is destroyed
+ // during the emit, we can still check for head.next
+ const head = this._head;
+
// Invoke listeners added to internal emitter
- this._emitter.emit(TICK, this.deltaTime);
+ let listener = head.next;
+
+ while (listener)
+ {
+ listener = listener.emit(this.deltaTime);
+ }
+
+ if (!head.next)
+ {
+ this._cancelIfNeeded();
+ }
}
else
{
diff --git a/src/core/ticker/TickerListener.js b/src/core/ticker/TickerListener.js
new file mode 100644
index 0000000..2bedb34
--- /dev/null
+++ b/src/core/ticker/TickerListener.js
@@ -0,0 +1,158 @@
+/**
+ * Internal class for handling the priority sorting of ticker handlers.
+ *
+ * @private
+ * @class
+ * @memberof PIXI.ticker
+ */
+export default class TickerListener
+{
+ /**
+ * Constructor
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} [context=null] - The listener context
+ * @param {number} [priority=0] - The priority for emitting
+ * @param {boolean} [once=false] - If the handler should fire once
+ */
+ constructor(fn, context = null, priority = 0, once = false)
+ {
+ /**
+ * The handler function to execute.
+ * @member {Function}
+ */
+ this.fn = fn;
+
+ /**
+ * The calling to execute.
+ * @member {Function}
+ */
+ this.context = context;
+
+ /**
+ * The current priority.
+ * @member {number}
+ */
+ this.priority = priority;
+
+ /**
+ * If this should only execute once.
+ * @member {boolean}
+ */
+ this.once = once;
+
+ /**
+ * The next item in chain.
+ * @member {TickerListener}
+ */
+ this.next = null;
+
+ /**
+ * The previous item in chain.
+ * @member {TickerListener}
+ */
+ this.previous = null;
+
+ /**
+ * `true` if this listener has been destroyed already.
+ * @member {boolean}
+ * @private
+ */
+ this._destroyed = false;
+ }
+
+ /**
+ * Simple compare function to figure out if a function and context match.
+ *
+ * @param {Function} fn - The listener function to be added for one update
+ * @param {Function} context - The listener context
+ * @return {boolean} `true` if the listener match the arguments
+ */
+ match(fn, context)
+ {
+ context = context || null;
+
+ return this.fn === fn && this.context === context;
+ }
+
+ /**
+ * Emit by calling the current function.
+ * @param {number} deltaTime - time since the last emit.
+ * @return {TickerListener} Next ticker
+ */
+ emit(deltaTime)
+ {
+ if (this.context)
+ {
+ this.fn.call(this.context, deltaTime);
+ }
+ else
+ {
+ this.fn(deltaTime);
+ }
+
+ if (this.once)
+ {
+ this.destroy();
+ }
+
+ const redirect = this.next;
+
+ // Soft-destroying should remove
+ // the next reference
+ if (this._destroyed)
+ {
+ this.next = null;
+ }
+
+ return redirect;
+ }
+
+ /**
+ * Connect to the list.
+ * @param {TickerListener} previous - Input node, previous listener
+ */
+ connect(previous)
+ {
+ this.previous = previous;
+ if (previous.next)
+ {
+ previous.next.previous = this;
+ }
+ this.next = previous.next;
+ previous.next = this;
+ }
+
+ /**
+ * Destroy and don't use after this.
+ * @param {boolean} [hard = false] `true` to remove the `next` reference, this
+ * is considered a hard destroy. Soft destroy maintains the next reference.
+ * @return {TickerListener} The listener to redirect while emitting or removing.
+ */
+ destroy(hard = false)
+ {
+ this._destroyed = true;
+ this.fn = null;
+ this.context = null;
+
+ // Disconnect, hook up next and previous
+ if (this.previous)
+ {
+ this.previous.next = this.next;
+ }
+
+ if (this.next)
+ {
+ this.next.previous = this.previous;
+ }
+
+ // Redirect to the next item
+ const redirect = this.previous;
+
+ // Remove references
+ this.next = hard ? null : redirect;
+ this.previous = null;
+
+ return redirect;
+ }
+}
diff --git a/src/core/ticker/index.js b/src/core/ticker/index.js
index 4b3017c..c3020d4 100644
--- a/src/core/ticker/index.js
+++ b/src/core/ticker/index.js
@@ -45,8 +45,25 @@
const shared = new Ticker();
shared.autoStart = true;
+shared.destroy = () =>
+{
+ // protect destroying shared ticker
+ // this is used by other internal systems
+ // like AnimatedSprite and InteractionManager
+};
/**
+ * This namespace contains an API for interacting with PIXI's internal global update loop.
+ *
+ * This ticker is used for rendering, {@link PIXI.extras.AnimatedSprite AnimatedSprite},
+ * {@link PIXI.interaction.InteractionManager InteractionManager} and many other time-based PIXI systems.
+ * @example
+ * const ticker = new PIXI.ticker.Ticker();
+ * ticker.stop();
+ * ticker.add((deltaTime) => {
+ * // do something every frame
+ * });
+ * ticker.start();
* @namespace PIXI.ticker
*/
export { shared, Ticker };
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 99e585a..098b383 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -9,6 +9,21 @@
let saidHello = false;
/**
+ * Generalized convenience utilities for PIXI.
+ * @example
+ * // Extend PIXI's internal Event Emitter.
+ * class MyEmitter extends PIXI.utils.EventEmitter {
+ * constructor() {
+ * super();
+ * console.log("Emitter created!");
+ * }
+ * }
+ *
+ * // Get info on current device
+ * console.log(PIXI.utils.isMobile);
+ *
+ * // Convert hex color to string
+ * console.log(PIXI.utils.hex2string(0xff00ff)); // returns: "#ff00ff"
* @namespace PIXI.utils
*/
export {
@@ -353,7 +368,7 @@
* @memberof PIXI.utils
* @private
*/
-export const TextureCache = {};
+export const TextureCache = Object.create(null);
/**
* @todo Describe property usage
@@ -361,7 +376,7 @@
* @memberof PIXI.utils
* @private
*/
-export const BaseTextureCache = {};
+export const BaseTextureCache = Object.create(null);
/**
* Destroys all texture in the cache
diff --git a/src/deprecation.js b/src/deprecation.js
index b9c73b3..0a9a771 100644
--- a/src/deprecation.js
+++ b/src/deprecation.js
@@ -695,6 +695,49 @@
};
/**
+ * Calculates the ascent, descent and fontSize of a given fontStyle
+ *
+ * @name PIXI.Text.calculateFontProperties
+ * @see PIXI.TextMetrics.measureFont
+ * @deprecated since version 4.5.0
+ * @param {string} font - String representing the style of the font
+ * @return {Object} Font properties object
+ */
+core.Text.calculateFontProperties = function calculateFontProperties(font)
+{
+ warn(`Text.calculateFontProperties is now deprecated, please use the TextMetrics.measureFont`);
+
+ return core.TextMetrics.measureFont(font);
+};
+
+Object.defineProperties(core.Text, {
+ fontPropertiesCache: {
+ get()
+ {
+ warn(`Text.fontPropertiesCache is deprecated`);
+
+ return core.TextMetrics._fonts;
+ },
+ },
+ fontPropertiesCanvas: {
+ get()
+ {
+ warn(`Text.fontPropertiesCanvas is deprecated`);
+
+ return core.TextMetrics._canvas;
+ },
+ },
+ fontPropertiesContext: {
+ get()
+ {
+ warn(`Text.fontPropertiesContext is deprecated`);
+
+ return core.TextMetrics._context;
+ },
+ },
+});
+
+/**
* @method
* @name PIXI.Text#setStyle
* @see PIXI.Text#style
@@ -710,7 +753,7 @@
/**
* @method
* @name PIXI.Text#determineFontProperties
- * @see PIXI.Text#calculateFontProperties
+ * @see PIXI.Text#measureFontProperties
* @deprecated since version 4.2.0
* @private
* @param {string} fontStyle - String representing the style of the font
@@ -718,10 +761,31 @@
*/
core.Text.prototype.determineFontProperties = function determineFontProperties(fontStyle)
{
- warn('determineFontProperties is now deprecated, please use the static calculateFontProperties method, '
- + 'e.g : Text.calculateFontProperties(fontStyle);');
+ warn('determineFontProperties is now deprecated, please use TextMetrics.measureFont method');
- return core.Text.calculateFontProperties(fontStyle);
+ return core.TextMetrics.measureFont(fontStyle);
+};
+
+/**
+ * @method
+ * @name PIXI.Text.getFontStyle
+ * @see PIXI.TextMetrics.getFontStyle
+ * @deprecated since version 4.5.0
+ * @param {PIXI.TextStyle} style - The style to use.
+ * @return {string} Font string
+ */
+core.Text.getFontStyle = function getFontStyle(style)
+{
+ warn('getFontStyle is now deprecated, please use TextStyle.toFontString() instead');
+
+ style = style || {};
+
+ if (!(style instanceof core.TextStyle))
+ {
+ style = new core.TextStyle(style);
+ }
+
+ return style.toFontString();
};
Object.defineProperties(core.TextStyle.prototype, {
@@ -830,6 +894,41 @@
warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;');
};
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.addTextureToCache
+ * @see PIXI.Texture.addToCache
+ * @deprecated since 4.5.0
+ * @param {PIXI.Texture} texture - The Texture to add to the cache.
+ * @param {string} id - The id that the texture will be stored against.
+ */
+core.Texture.addTextureToCache = function addTextureToCache(texture, id)
+{
+ core.Texture.addToCache(texture, id);
+ warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.');
+};
+
+/**
+ * @static
+ * @function
+ * @name PIXI.Texture.removeTextureFromCache
+ * @see PIXI.Texture.removeFromCache
+ * @deprecated since 4.5.0
+ * @param {string} id - The id of the texture to be removed
+ * @return {PIXI.Texture|null} The texture that was removed
+ */
+core.Texture.removeTextureFromCache = function removeTextureFromCache(id)
+{
+ warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. '
+ + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. '
+ + 'For that, use BaseTexture.removeFromCache');
+
+ core.BaseTexture.removeFromCache(id);
+
+ return core.Texture.removeFromCache(id);
+};
+
Object.defineProperties(filters, {
/**
@@ -918,6 +1017,35 @@
});
/**
+ * @method
+ * @name PIXI.prepare.BasePrepare#register
+ * @see PIXI.prepare.BasePrepare#registerFindHook
+ * @deprecated since version 4.4.2
+ * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+prepare.BasePrepare.prototype.register = function register(addHook, uploadHook)
+{
+ warn('renderer.plugins.prepare.register is now deprecated, '
+ + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook');
+
+ if (addHook)
+ {
+ this.registerFindHook(addHook);
+ }
+
+ if (uploadHook)
+ {
+ this.registerUploadHook(uploadHook);
+ }
+
+ return this;
+};
+
+/**
* The number of graphics or textures to upload to the GPU.
*
* @name PIXI.prepare.canvas.UPLOADS_PER_FRAME
@@ -969,67 +1097,73 @@
},
});
-Object.defineProperties(loaders.Resource.prototype, {
- isJson: {
- get()
- {
- warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+if (loaders.Loader)
+{
+ const Resource = loaders.Resource;
+ const Loader = loaders.Loader;
- return this.type === loaders.Loader.Resource.TYPE.JSON;
+ Object.defineProperties(Resource.prototype, {
+ isJson: {
+ get()
+ {
+ warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.');
+
+ return this.type === Resource.TYPE.JSON;
+ },
},
- },
- isXml: {
- get()
- {
- warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
+ isXml: {
+ get()
+ {
+ warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.');
- return this.type === loaders.Loader.Resource.TYPE.XML;
+ return this.type === Resource.TYPE.XML;
+ },
},
- },
- isImage: {
- get()
- {
- warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
+ isImage: {
+ get()
+ {
+ warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.');
- return this.type === loaders.Loader.Resource.TYPE.IMAGE;
+ return this.type === Resource.TYPE.IMAGE;
+ },
},
- },
- isAudio: {
- get()
- {
- warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
+ isAudio: {
+ get()
+ {
+ warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.');
- return this.type === loaders.Loader.Resource.TYPE.AUDIO;
+ return this.type === Resource.TYPE.AUDIO;
+ },
},
- },
- isVideo: {
- get()
- {
- warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
+ isVideo: {
+ get()
+ {
+ warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.');
- return this.type === loaders.Loader.Resource.TYPE.VIDEO;
+ return this.type === Resource.TYPE.VIDEO;
+ },
},
- },
-});
+ });
-Object.defineProperties(loaders.Loader.prototype, {
- before: {
- get()
- {
- warn('The before() method is deprecated, please use pre().');
+ Object.defineProperties(Loader.prototype, {
+ before: {
+ get()
+ {
+ warn('The before() method is deprecated, please use pre().');
- return this.pre;
+ return this.pre;
+ },
},
- },
- after: {
- get()
- {
- warn('The after() method is deprecated, please use use().');
+ after: {
+ get()
+ {
+ warn('The after() method is deprecated, please use use().');
- return this.use;
+ return this.use;
+ },
},
- },
-});
+ });
+}
/**
* @name PIXI.interaction.interactiveTarget#defaultCursor
diff --git a/src/extract/canvas/CanvasExtract.js b/src/extract/canvas/CanvasExtract.js
index 941ca05..3ac5aee 100644
--- a/src/extract/canvas/CanvasExtract.js
+++ b/src/extract/canvas/CanvasExtract.js
@@ -8,7 +8,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class CanvasExtract
{
@@ -21,9 +21,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.CanvasExtract} extract
+ * @member {PIXI.extract.CanvasExtract} extract
* @memberof PIXI.CanvasRenderer#
- * @see PIXI.CanvasExtract
+ * @see PIXI.extract.CanvasExtract
*/
renderer.extract = this;
}
diff --git a/src/extract/index.js b/src/extract/index.js
index 0f2170d..e9478c1 100644
--- a/src/extract/index.js
+++ b/src/extract/index.js
@@ -1,2 +1,22 @@
+/**
+ * This namespace provides renderer-specific plugins for exporting content from a renderer.
+ * For instance, these plugins can be used for saving an Image, Canvas element or for exporting the raw image data (pixels).
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new app (will auto-add extract plugin to renderer)
+ * const app = new PIXI.Application();
+ *
+ * // Draw a red circle
+ * const graphics = new PIXI.Graphics()
+ * .beginFill(0xFF0000)
+ * .drawCircle(0, 0, 50);
+ *
+ * // Render the graphics as an HTMLImageElement
+ * const image = app.renderer.plugins.extract.image(graphics);
+ * document.body.appendChild(image);
+ * @namespace PIXI.extract
+ */
export { default as webgl } from './webgl/WebGLExtract';
export { default as canvas } from './canvas/CanvasExtract';
diff --git a/src/extract/webgl/WebGLExtract.js b/src/extract/webgl/WebGLExtract.js
index 60228bb..1c3dd30 100644
--- a/src/extract/webgl/WebGLExtract.js
+++ b/src/extract/webgl/WebGLExtract.js
@@ -9,7 +9,7 @@
* An instance of this class is automatically created by default, and can be found at renderer.plugins.extract
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extract
*/
export default class WebGLExtract
{
@@ -22,9 +22,9 @@
/**
* Collection of methods for extracting data (image, pixels, etc.) from a display object or render texture
*
- * @member {PIXI.WebGLExtract} extract
+ * @member {PIXI.extract.WebGLExtract} extract
* @memberof PIXI.WebGLRenderer#
- * @see PIXI.WebGLExtract
+ * @see PIXI.extract.WebGLExtract
*/
renderer.extract = this;
}
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index 13b804f..5297778 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -137,7 +137,7 @@
this.playing = true;
if (this._autoUpdate)
{
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.HIGH);
}
}
diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js
index 78e6e40..16536d2 100644
--- a/src/extras/TextureTransform.js
+++ b/src/extras/TextureTransform.js
@@ -65,8 +65,36 @@
}
/**
+ * Multiplies uvs array to transform
+ * @param {Float32Array} uvs mesh uvs
+ * @param {Float32Array} [out=uvs] output
+ * @returns {Float32Array} output
+ */
+ multiplyUvs(uvs, out)
+ {
+ if (out === undefined)
+ {
+ out = uvs;
+ }
+
+ const mat = this.mapCoord;
+
+ for (let i = 0; i < uvs.length; i += 2)
+ {
+ const x = uvs[i];
+ const y = uvs[i + 1];
+
+ out[i] = (x * mat.a) + (y * mat.c) + mat.tx;
+ out[i + 1] = (x * mat.b) + (y * mat.d) + mat.ty;
+ }
+
+ return out;
+ }
+
+ /**
* updates matrices if texture was changed
* @param {boolean} forceUpdate if true, matrices will be updated any case
+ * @returns {boolean} whether or not it was updated
*/
update(forceUpdate)
{
@@ -74,13 +102,13 @@
if (!tex || !tex.valid)
{
- return;
+ return false;
}
if (!forceUpdate
&& this._lastTextureID === tex._updateID)
{
- return;
+ return false;
}
this._lastTextureID = tex._updateID;
@@ -110,5 +138,7 @@
frame[3] = (tex._frame.y + tex._frame.height - margin + offset) / texBase.height;
this.uClampOffset[0] = offset / texBase.realWidth;
this.uClampOffset[1] = offset / texBase.realHeight;
+
+ return true;
}
}
diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js
index fe82451..f31c2b6 100644
--- a/src/extras/TilingSprite.js
+++ b/src/extras/TilingSprite.js
@@ -180,9 +180,9 @@
const transform = this.worldTransform;
const resolution = renderer.resolution;
const baseTexture = texture.baseTexture;
- const baseTextureResolution = texture.baseTexture.resolution;
- const modX = (this.tilePosition.x / this.tileScale.x) % texture._frame.width;
- const modY = (this.tilePosition.y / this.tileScale.y) % texture._frame.height;
+ const baseTextureResolution = baseTexture.resolution;
+ const modX = ((this.tilePosition.x / this.tileScale.x) % texture._frame.width) * baseTextureResolution;
+ const modY = ((this.tilePosition.y / this.tileScale.y) % texture._frame.height) * baseTextureResolution;
// create a nice shiny pattern!
// TODO this needs to be refreshed if texture changes..
diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js
index 37e55d2..4a165a3 100644
--- a/src/extras/cacheAsBitmap.js
+++ b/src/extras/cacheAsBitmap.js
@@ -1,4 +1,7 @@
import * as core from '../core';
+import Texture from '../core/textures/Texture';
+import BaseTexture from '../core/textures/BaseTexture';
+import { uid } from '../core/utils';
const DisplayObject = core.DisplayObject;
const _tempMatrix = new core.Matrix();
@@ -20,6 +23,8 @@
*/
constructor()
{
+ this.textureCacheId = null;
+
this.originalRenderWebGL = null;
this.originalRenderCanvas = null;
this.originalCalculateBounds = null;
@@ -185,6 +190,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -227,7 +239,16 @@
this.transform._parentID = -1;
// restore the transform of the cached sprite to avoid the nasty flicker..
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
// map the hit test..
this.containsPoint = cachedSprite.containsPoint.bind(cachedSprite);
@@ -280,6 +301,13 @@
const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0);
+ const textureCacheId = `cacheAsBitmap_${uid()}`;
+
+ this._cacheData.textureCacheId = textureCacheId;
+
+ BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId);
+ Texture.addToCache(renderTexture, textureCacheId);
+
// need to set //
const m = _tempMatrix;
@@ -314,7 +342,17 @@
cachedSprite._bounds = this._bounds;
cachedSprite.alpha = cacheAlpha;
- this.updateTransform();
+ if (!this.parent)
+ {
+ this.parent = renderer._tempDisplayObjectParent;
+ this.updateTransform();
+ this.parent = null;
+ }
+ else
+ {
+ this.updateTransform();
+ }
+
this.updateTransform = this.displayObjectUpdateTransform;
this._cacheData.sprite = cachedSprite;
@@ -352,6 +390,11 @@
{
this._cacheData.sprite._texture.destroy(true);
this._cacheData.sprite = null;
+
+ BaseTexture.removeFromCache(this._cacheData.textureCacheId);
+ Texture.removeFromCache(this._cacheData.textureCacheId);
+
+ this._cacheData.textureCacheId = null;
};
/**
diff --git a/src/extras/index.js b/src/extras/index.js
index bbd8bbf..a34d7ec 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -1,7 +1,7 @@
/**
+ * Additional PIXI DisplayObjects for animation, tiling and bitmap text.
* @namespace PIXI.extras
*/
-export { default as TextureTransform } from './TextureTransform';
export { default as AnimatedSprite } from './AnimatedSprite';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js
index 2591719..5734fc0 100644
--- a/src/extras/webgl/TilingSpriteRenderer.js
+++ b/src/extras/webgl/TilingSpriteRenderer.js
@@ -10,7 +10,7 @@
* WebGL renderer plugin for tiling sprites
*
* @class
- * @memberof PIXI
+ * @memberof PIXI.extras
* @extends PIXI.ObjectRenderer
*/
export default class TilingSpriteRenderer extends core.ObjectRenderer
@@ -131,7 +131,7 @@
tempMat.invert();
if (isSimple)
{
- tempMat.append(uv.mapCoord);
+ tempMat.prepend(uv.mapCoord);
}
else
{
diff --git a/src/filters/displacement/DisplacementFilter.js b/src/filters/displacement/DisplacementFilter.js
index d5355c3..b5d030c 100644
--- a/src/filters/displacement/DisplacementFilter.js
+++ b/src/filters/displacement/DisplacementFilter.js
@@ -35,7 +35,7 @@
this.maskSprite = sprite;
this.maskMatrix = maskMatrix;
- this.uniforms.mapSampler = sprite.texture;
+ this.uniforms.mapSampler = sprite._texture;
this.uniforms.filterMatrix = maskMatrix;
this.uniforms.scale = { x: 1, y: 1 };
diff --git a/src/filters/index.js b/src/filters/index.js
index f3e8862..7ceb50a 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -1,4 +1,21 @@
/**
+ * This namespace contains WebGL-only display filters that can be applied
+ * to DisplayObjects using the {@link PIXI.DisplayObject#filters filters} property.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ *
+ * // Draw a green rectangle
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add a blur filter
+ * rect.filters = [new PIXI.filters.BlurFilter()];
+ *
+ * // Display rectangle
+ * app.stage.addChild(rect);
+ * document.body.appendChild(app.view);
* @namespace PIXI.filters
*/
export { default as FXAAFilter } from './fxaa/FXAAFilter';
diff --git a/src/index.js b/src/index.js
index c553427..77da490 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,6 +20,14 @@
import { utils } from './core';
utils.mixins.performMixins();
+/**
+ * Alias for {@link PIXI.loaders.shared}.
+ * @name loader
+ * @memberof PIXI
+ * @type {PIXI.loader.Loader}
+ */
+const loader = loaders.shared || null;
+
export {
accessibility,
extract,
@@ -30,18 +38,8 @@
mesh,
particles,
prepare,
+ loader,
};
-/**
- * A premade instance of the loader that can be used to load resources.
- *
- * @name loader
- * @memberof PIXI
- * @property {PIXI.loaders.Loader}
- */
-const loader = loaders && loaders.Loader ? new loaders.Loader() : null; // check is there in case user excludes loader lib
-
-export { loader };
-
// Always export pixi globally.
global.PIXI = exports; // eslint-disable-line
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index 818da35..f4619a1 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -30,7 +30,10 @@
/**
* When passed to an event handler, this will be the original DOM Event that was captured
*
- * @member {Event}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent
+ * @member {MouseEvent|TouchEvent|PointerEvent}
*/
this.originalEvent = null;
diff --git a/src/interaction/InteractionEvent.js b/src/interaction/InteractionEvent.js
index dc554f9..a7be332 100644
--- a/src/interaction/InteractionEvent.js
+++ b/src/interaction/InteractionEvent.js
@@ -33,14 +33,14 @@
*/
this.currentTarget = null;
- /*
+ /**
* Type of the event
*
* @member {string}
*/
this.type = null;
- /*
+ /**
* InteractionData related to this event
*
* @member {PIXI.interaction.InteractionData}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 610e248..e1c50d7 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -13,6 +13,14 @@
const MOUSE_POINTER_ID = 'MOUSE';
+// helpers for hitTest() - only used inside hitTest()
+const hitTestEvent = {
+ target: null,
+ data: {
+ global: null,
+ },
+};
+
/**
* The interaction manager deals with mouse, touch and pointer events. Any DisplayObject can be interactive
* if its interactive parameter is set to true
@@ -245,67 +253,60 @@
this.setTargetElement(this.renderer.view, this.renderer.resolution);
/**
- * Fired when a pointer device button (usually a mouse button) is pressed on the display
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display
* object.
*
- * @event mousedown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* on the display object.
*
- * @event rightdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released over the display
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
* object.
*
- * @event mouseup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is released
* over the display object.
*
- * @event rightup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is pressed and released on
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
* the display object.
*
- * @event click
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device secondary button (usually a mouse right-button) is pressed
* and released on the display object.
*
- * @event rightclick
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
- * Fired when a pointer device button (usually a mouse button) is released outside the
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
* display object that initially registered a
* [mousedown]{@link PIXI.interaction.InteractionManager#event:mousedown}.
*
- * @event mouseupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
@@ -313,146 +314,362 @@
* outside the display object that initially registered a
* [rightdown]{@link PIXI.interaction.InteractionManager#event:rightdown}.
*
- * @event rightupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved while over the display object
*
- * @event mousemove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved onto the display object
*
- * @event mouseover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device (usually a mouse) is moved off the display object
*
- * @event mouseout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed on the display object.
*
- * @event pointerdown
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released over the display object.
*
- * @event pointerup
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a pointer event
*
- * @event pointercancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is pressed and released on the display object.
*
- * @event pointertap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device button is released outside the display object that initially
* registered a [pointerdown]{@link PIXI.interaction.InteractionManager#event:pointerdown}.
*
- * @event pointerupoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved while over the display object
*
- * @event pointermove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved onto the display object
*
- * @event pointerover
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a pointer device is moved off the display object
*
- * @event pointerout
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed on the display object.
*
- * @event touchstart
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed from the display object.
*
- * @event touchend
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when the operating system cancels a touch
*
- * @event touchcancel
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is placed and removed from the display object.
*
- * @event tap
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is removed outside of the display object that initially
* registered a [touchstart]{@link PIXI.interaction.InteractionManager#event:touchstart}.
*
- * @event touchendoutside
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
/**
* Fired when a touch point is moved along the display object.
*
- * @event touchmove
- * @type {PIXI.interaction.InteractionData}
- * @memberof PIXI.interaction.InteractionManager#
+ * @event PIXI.interaction.InteractionManager#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
*/
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed on the display.
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousedown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released over the display
+ * object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * over the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is pressed and released on
+ * the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#click
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is pressed
+ * and released on the display object. DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightclick
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button (usually a mouse left-button) is released outside the
+ * display object that initially registered a
+ * [mousedown]{@link PIXI.DisplayObject#event:mousedown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device secondary button (usually a mouse right-button) is released
+ * outside the display object that initially registered a
+ * [rightdown]{@link PIXI.DisplayObject#event:rightdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#rightupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mousemove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device (usually a mouse) is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#mouseout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerdown
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerup
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a pointer event.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointercancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is pressed and released on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointertap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device button is released outside the display object that initially
+ * registered a [pointerdown]{@link PIXI.DisplayObject#event:pointerdown}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerupoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved while over the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointermove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved onto the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerover
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a pointer device is moved off the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#pointerout
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed on the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchstart
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchend
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when the operating system cancels a touch.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchcancel
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is placed and removed from the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#tap
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is removed outside of the display object that initially
+ * registered a [touchstart]{@link PIXI.DisplayObject#event:touchstart}.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchendoutside
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+
+ /**
+ * Fired when a touch point is moved along the display object.
+ * DisplayObject's `interactive` property must be set to `true` to fire event.
+ *
+ * @event PIXI.DisplayObject#touchmove
+ * @param {PIXI.interaction.InteractionEvent} event - Interaction event
+ */
+ }
+
+ /**
+ * Hit tests a point against the display tree, returning the first interactive object that is hit.
+ *
+ * @param {PIXI.Point} globalPoint - A point to hit test with, in global space.
+ * @param {PIXI.Container} [root] - The root display object to start from. If omitted, defaults
+ * to the last rendered root of the associated renderer.
+ * @return {PIXI.DisplayObject} The hit display object, if any.
+ */
+ hitTest(globalPoint, root)
+ {
+ // clear the target for our hit test
+ hitTestEvent.target = null;
+ // assign the global point
+ hitTestEvent.data.global = globalPoint;
+ // ensure safety of the root
+ if (!root)
+ {
+ root = this.renderer._lastObjectRendered;
+ }
+ // run the hit test
+ this.processInteractive(hitTestEvent, root, null, true);
+ // return our found object - it'll be null if we didn't hit anything
+
+ return hitTestEvent.target;
}
/**
@@ -487,7 +704,7 @@
return;
}
- core.ticker.shared.add(this.update, this);
+ core.ticker.shared.add(this.update, this, core.UPDATE_PRIORITY.INTERACTION);
if (window.navigator.msPointerEnabled)
{
@@ -753,7 +970,7 @@
* testing the interactive objects and passes the hit across in the function.
*
* @private
- * @param {InteractionEvent} interactionEvent - event containing the point that
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - event containing the point that
* is tested for collision
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - the displayObject
* that will be hit test (recursively crawls its children)
@@ -881,7 +1098,10 @@
interactionEvent.target = displayObject;
}
- func(interactionEvent, displayObject, !!hit);
+ if (func)
+ {
+ func(interactionEvent, displayObject, !!hit);
+ }
}
}
@@ -943,7 +1163,7 @@
* Processes the result of the pointer down check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1040,7 +1260,7 @@
* Processes the result of the pointer cancel check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
*/
processPointerCancel(interactionEvent, displayObject)
@@ -1076,7 +1296,7 @@
* Processes the result of the pointer up check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1214,7 +1434,7 @@
* Processes the result of the pointer move check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1283,7 +1503,7 @@
* Processes the result of the pointer over/out check and dispatches the event if need be
*
* @private
- * @param {InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The interaction event wrapping the DOM event
* @param {PIXI.Container|PIXI.Sprite|PIXI.extras.TilingSprite} displayObject - The display object that was tested
* @param {boolean} hit - the result of the hit test on the display object
*/
@@ -1376,7 +1596,7 @@
*
* @private
* @param {PointerEvent} event - Normalized pointer event, output from normalizeToPointerData
- * @return {InteractionData} - Interaction data for the given pointer identifier
+ * @return {PIXI.interaction.InteractionData} - Interaction data for the given pointer identifier
*/
getInteractionDataForPointerId(event)
{
@@ -1420,10 +1640,11 @@
* Configure an InteractionEvent to wrap a DOM PointerEvent and InteractionData
*
* @private
- * @param {InteractionEvent} interactionEvent - The event to be configured
+ * @param {PIXI.interaction.InteractionEvent} interactionEvent - The event to be configured
* @param {PointerEvent} pointerEvent - The DOM event that will be paired with the InteractionEvent
- * @param {InteractionData} interactionData - The InteractionData that will be paired with the InteractionEvent
- * @return {InteractionEvent} the interaction event that was passed in
+ * @param {PIXI.interaction.InteractionData} interactionData - The InteractionData that will be paired
+ * with the InteractionEvent
+ * @return {PIXI.interaction.InteractionEvent} the interaction event that was passed in
*/
configureInteractionEventForDOMEvent(interactionEvent, pointerEvent, interactionData)
{
diff --git a/src/interaction/InteractionTrackingData.js b/src/interaction/InteractionTrackingData.js
index 6a1f69e..8493b63 100644
--- a/src/interaction/InteractionTrackingData.js
+++ b/src/interaction/InteractionTrackingData.js
@@ -35,8 +35,10 @@
}
/**
+ * Unique pointer id of the event
+ *
* @readonly
- * @type {number} Unique pointer id of the event
+ * @member {number}
*/
get pointerId()
{
diff --git a/src/interaction/index.js b/src/interaction/index.js
index 56c5b09..72f7d71 100644
--- a/src/interaction/index.js
+++ b/src/interaction/index.js
@@ -1,4 +1,8 @@
/**
+ * This namespace contains a renderer plugin for handling mouse, pointer, and touch events.
+ *
+ * Do not instantiate this plugin directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
* @namespace PIXI.interaction
*/
export { default as InteractionData } from './InteractionData';
diff --git a/src/interaction/interactiveTarget.js b/src/interaction/interactiveTarget.js
index d252fba..cebac40 100644
--- a/src/interaction/interactiveTarget.js
+++ b/src/interaction/interactiveTarget.js
@@ -2,7 +2,7 @@
* Default property values of interactive objects
* Used by {@link PIXI.interaction.InteractionManager} to automatically give all DisplayObjects these properties
*
- * @mixin
+ * @private
* @name interactiveTarget
* @memberof PIXI.interaction
* @example
@@ -14,18 +14,28 @@
* );
*/
export default {
+
/**
- * Determines if the displayObject be clicked/touched
+ * Enable interaction events for the DisplayObject. Touch, pointer and mouse
+ * events will not be emitted unless `interactive` is set to `true`.
*
- * @inner {boolean}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.on('tap', (event) => {
+ * //handle event
+ * });
+ * @member {boolean}
+ * @memberof PIXI.DisplayObject#
*/
interactive: false,
/**
* Determines if the children to the displayObject can be clicked/touched
- * Setting this to false allows pixi to bypass a recursive hitTest function
+ * Setting this to false allows pixi to bypass a recursive `hitTest` function
*
- * @inner {boolean}
+ * @member {boolean}
+ * @memberof PIXI.Container#
*/
interactiveChildren: true,
@@ -33,7 +43,12 @@
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the displayObject's bounds.
*
- * @inner {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.hitArea = new PIXI.Rectangle(0, 0, 100, 100);
+ * @member {PIXI.Rectangle|PIXI.Circle|PIXI.Ellipse|PIXI.Polygon|PIXI.RoundedRectangle}
+ * @memberof PIXI.DisplayObject#
*/
hitArea: null,
@@ -41,8 +56,12 @@
* If enabled, the mouse cursor use the pointer behavior when hovered over the displayObject if it is interactive
* Setting this changes the 'cursor' property to `'pointer'`.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.buttonMode = true;
* @member {boolean}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
*/
get buttonMode()
{
@@ -64,9 +83,14 @@
* This defines what cursor mode is used when the mouse cursor
* is hovered over the displayObject.
*
+ * @example
+ * const sprite = new PIXI.Sprite(texture);
+ * sprite.interactive = true;
+ * sprite.cursor = 'wait';
* @see https://developer.mozilla.org/en/docs/Web/CSS/cursor
*
- * @inner {string}
+ * @member {string}
+ * @memberof PIXI.DisplayObject#
*/
cursor: null,
@@ -74,7 +98,7 @@
* Internal set of all active pointers, by identifier
*
* @member {Map}
- * @memberof PIXI.interaction.interactiveTarget#
+ * @memberof PIXI.DisplayObject#
* @private
*/
get trackedPointers()
@@ -87,7 +111,8 @@
/**
* Map of all tracked pointers, by identifier. Use trackedPointers to access.
*
- * @private {Map}
+ * @private
+ * @type {Map}
*/
_trackedPointers: undefined,
};
diff --git a/src/loaders/index.js b/src/loaders/index.js
index 5189793..daa1fc4 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -1,9 +1,22 @@
+import Application from '../core/Application';
+import Loader from './loader';
+
/**
+ * This namespace contains APIs which extends the {@link https://github.com/englercj/resource-loader resource-loader} module
+ * for loading assets, data, and other resources dynamically.
+ * @example
+ * const loader = new PIXI.loaders.Loader();
+ * loader.add('bunny', 'data/bunny.png')
+ * .add('spaceship', 'assets/spritesheet.json');
+ * loader.load((loader, resources) => {
+ * // resources.bunny
+ * // resources.spaceship
+ * });
* @namespace PIXI.loaders
*/
-export { default as Loader } from './loader';
+export { Loader };
export { default as bitmapFontParser, parse as parseBitmapFontData } from './bitmapFontParser';
-export { default as spritesheetParser } from './spritesheetParser';
+export { default as spritesheetParser, getResourcePath } from './spritesheetParser';
export { default as textureParser } from './textureParser';
/**
@@ -13,3 +26,55 @@
* @memberof PIXI.loaders
*/
export { Resource } from 'resource-loader';
+
+/**
+ * A premade instance of the loader that can be used to load resources.
+ * @name shared
+ * @memberof PIXI.loaders
+ * @type {PIXI.loaders.Loader}
+ */
+const shared = new Loader();
+
+shared.destroy = () =>
+{
+ // protect destroying shared loader
+};
+
+export { shared };
+
+// Mixin the loader construction
+const AppPrototype = Application.prototype;
+
+AppPrototype._loader = null;
+
+/**
+ * Loader instance to help with asset loading.
+ * @name PIXI.Application#loader
+ * @type {PIXI.loaders.Loader}
+ */
+Object.defineProperty(AppPrototype, 'loader', {
+ get()
+ {
+ if (!this._loader)
+ {
+ const sharedLoader = this._options.sharedLoader;
+
+ this._loader = sharedLoader ? shared : new Loader();
+ }
+
+ return this._loader;
+ },
+});
+
+// Override the destroy function
+// making sure to destroy the current Loader
+AppPrototype._parentDestroy = AppPrototype.destroy;
+AppPrototype.destroy = function destroy()
+{
+ if (this._loader)
+ {
+ this._loader.destroy();
+ this._loader = null;
+ }
+ this._parentDestroy();
+};
diff --git a/src/loaders/loader.js b/src/loaders/loader.js
index 767a411..59b8000 100644
--- a/src/loaders/loader.js
+++ b/src/loaders/loader.js
@@ -90,6 +90,15 @@
{
Loader._pixiMiddleware.push(fn);
}
+
+ /**
+ * Destroy the loader, removes references.
+ */
+ destroy()
+ {
+ this.removeAllListeners();
+ this.reset();
+ }
}
// Copy EE3 prototype (mixin)
diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js
index e57dfb2..eda9584 100644
--- a/src/loaders/spritesheetParser.js
+++ b/src/loaders/spritesheetParser.js
@@ -1,12 +1,11 @@
import { Resource } from 'resource-loader';
-import path from 'path';
+import url from 'url';
import { Spritesheet } from '../core';
export default function ()
{
return function spritesheetParser(resource, next)
{
- let resourcePath;
const imageResourceName = `${resource.name}_image`;
// skip if no data, its not json, it isn't spritesheet data, or the image resource already exists
@@ -28,15 +27,7 @@
parentResource: resource,
};
- // Prepend url path unless the resource image is a data url
- if (resource.isDataUrl)
- {
- resourcePath = resource.data.meta.image;
- }
- else
- {
- resourcePath = `${path.dirname(resource.url.replace(this.baseUrl, ''))}/${resource.data.meta.image}`;
- }
+ const resourcePath = getResourcePath(resource, this.baseUrl);
// load the image for this sheet
this.add(imageResourceName, resourcePath, loadOptions, function onImageLoad(res)
@@ -56,3 +47,14 @@
});
};
}
+
+export function getResourcePath(resource, baseUrl)
+{
+ // Prepend url path unless the resource image is a data url
+ if (resource.isDataUrl)
+ {
+ return resource.data.meta.image;
+ }
+
+ return url.resolve(resource.url.replace(baseUrl, ''), resource.data.meta.image);
+}
diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js
index 02c04a0..d866f03 100644
--- a/src/mesh/Mesh.js
+++ b/src/mesh/Mesh.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { default as TextureTransform } from '../extras/TextureTransform';
const tempPoint = new core.Point();
const tempPolygon = new core.Polygon();
@@ -28,7 +29,7 @@
* @member {PIXI.Texture}
* @private
*/
- this._texture = null;
+ this._texture = texture;
/**
* The Uvs of the Mesh
@@ -52,8 +53,10 @@
100, 100,
0, 100]);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
// TODO auto generate this based on draw mode!
this.indices = indices || new Uint16Array([0, 1, 3, 2]);
@@ -98,9 +101,6 @@
*/
this.drawMode = drawMode || Mesh.DRAW_MODES.TRIANGLE_MESH;
- // run texture setter;
- this.texture = texture;
-
/**
* The default shader that is used if a mesh doesn't have a more specific one.
*
@@ -125,9 +125,27 @@
this._glDatas = {};
/**
+ * transform that is applied to UV to get the texture coords
+ * its updated independently from texture uvTransform
+ * updates of uvs are tied to that thing
+ *
+ * @member {PIXI.extras.TextureTransform}
+ * @private
+ */
+ this._uvTransform = new TextureTransform(texture);
+
+ /**
+ * whether or not upload uvTransform to shader
+ * if its false, then uvs should be pre-multiplied
+ * if you change it for generated mesh, please call 'refresh(true)'
+ * @member {boolean}
+ * @default false
+ */
+ this.uploadUvTransform = false;
+
+ /**
* Plugin that is responsible for rendering this element.
* Allows to customize the rendering process without overriding '_renderWebGL' & '_renderCanvas' methods.
- *
* @member {string}
* @default 'mesh'
*/
@@ -142,6 +160,7 @@
*/
_renderWebGL(renderer)
{
+ this.refresh();
renderer.setObjectRenderer(renderer.plugins[this.pluginName]);
renderer.plugins[this.pluginName].render(this);
}
@@ -154,6 +173,7 @@
*/
_renderCanvas(renderer)
{
+ this.refresh();
renderer.plugins[this.pluginName].render(this);
}
@@ -164,6 +184,43 @@
*/
_onTextureUpdate()
{
+ this._uvTransform.texture = this._texture;
+ this.refresh();
+ }
+
+ /**
+ * multiplies uvs only if uploadUvTransform is false
+ * call it after you change uvs manually
+ * make sure that texture is valid
+ */
+ multiplyUvs()
+ {
+ if (!this.uploadUvTransform)
+ {
+ this._uvTransform.multiplyUvs(this.uvs);
+ }
+ }
+
+ /**
+ * Refreshes uvs for generated meshes (rope, plane)
+ * sometimes refreshes vertices too
+ *
+ * @param {boolean} [forceUpdate=false] if true, matrices will be updated any case
+ */
+ refresh(forceUpdate)
+ {
+ if (this._uvTransform.update(forceUpdate))
+ {
+ this._refresh();
+ }
+ }
+
+ /**
+ * re-calculates mesh coords
+ * @protected
+ */
+ _refresh()
+ {
/* empty */
}
diff --git a/src/mesh/NineSlicePlane.js b/src/mesh/NineSlicePlane.js
index 08ef76e..c243246 100644
--- a/src/mesh/NineSlicePlane.js
+++ b/src/mesh/NineSlicePlane.js
@@ -52,8 +52,8 @@
uvs[6] = uvs[14] = uvs[22] = uvs[30] = 1;
uvs[25] = uvs[27] = uvs[29] = uvs[31] = 1;
- this._origWidth = texture.width;
- this._origHeight = texture.height;
+ this._origWidth = texture.orig.width;
+ this._origHeight = texture.orig.height;
this._uvw = 1 / this._origWidth;
this._uvh = 1 / this._origHeight;
@@ -64,7 +64,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.width = texture.width;
+ this.width = this._origWidth;
/**
* The height of the NineSlicePlane, setting this will actually modify the vertices and UV's of this plane
@@ -73,7 +73,7 @@
* @memberof PIXI.NineSlicePlane#
* @override
*/
- this.height = texture.height;
+ this.height = this._origHeight;
uvs[2] = uvs[10] = uvs[18] = uvs[26] = this._uvw * leftWidth;
uvs[4] = uvs[12] = uvs[20] = uvs[28] = 1 - (this._uvw * rightWidth);
@@ -115,6 +115,8 @@
* @override
*/
this.bottomHeight = typeof bottomHeight !== 'undefined' ? bottomHeight : DEFAULT_BORDER_SIZE;
+
+ this.refresh(true);
}
/**
diff --git a/src/mesh/Plane.js b/src/mesh/Plane.js
index 3869a39..4f9b51c 100644
--- a/src/mesh/Plane.js
+++ b/src/mesh/Plane.js
@@ -43,17 +43,17 @@
}
/**
- * Refreshes
+ * Refreshes plane coordinates
*
*/
- refresh()
+ _refresh()
{
+ const texture = this._texture;
const total = this.verticesX * this.verticesY;
const verts = [];
const colors = [];
const uvs = [];
const indices = [];
- const texture = this.texture;
const segmentsX = this.verticesX - 1;
const segmentsY = this.verticesY - 1;
@@ -63,24 +63,12 @@
for (let i = 0; i < total; i++)
{
- if (texture._uvs)
- {
- const x = (i % this.verticesX);
- const y = ((i / this.verticesX) | 0);
+ const x = (i % this.verticesX);
+ const y = ((i / this.verticesX) | 0);
- verts.push((x * sizeX),
- (y * sizeY));
+ verts.push(x * sizeX, y * sizeY);
- // this works for rectangular textures.
- uvs.push(
- texture._uvs.x0 + ((texture._uvs.x1 - texture._uvs.x0) * (x / (this.verticesX - 1))),
- texture._uvs.y0 + ((texture._uvs.y3 - texture._uvs.y0) * (y / (this.verticesY - 1)))
- );
- }
- else
- {
- uvs.push(0);
- }
+ uvs.push(x / segmentsX, y / segmentsY);
}
// cons
@@ -106,8 +94,9 @@
this.uvs = new Float32Array(uvs);
this.colors = new Float32Array(colors);
this.indices = new Uint16Array(indices);
-
this.indexDirty = true;
+
+ this.multiplyUvs();
}
/**
diff --git a/src/mesh/Rope.js b/src/mesh/Rope.js
index 6ff8aaf..ccee263 100644
--- a/src/mesh/Rope.js
+++ b/src/mesh/Rope.js
@@ -1,5 +1,4 @@
import Mesh from './Mesh';
-import * as core from '../core';
/**
* The rope allows you to draw a texture across several points and them manipulate these points
@@ -26,39 +25,47 @@
{
super(texture);
- /*
- * @member {PIXI.Point[]} An array of points that determine the rope
+ /**
+ * An array of points that determine the rope
+ *
+ * @member {PIXI.Point[]}
*/
this.points = points;
- /*
- * @member {Float32Array} An array of vertices used to construct this rope.
+ /**
+ * An array of vertices used to construct this rope.
+ *
+ * @member {Float32Array}
*/
this.vertices = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} The WebGL Uvs of the rope.
+ /**
+ * The WebGL Uvs of the rope.
+ *
+ * @member {Float32Array}
*/
this.uvs = new Float32Array(points.length * 4);
- /*
- * @member {Float32Array} An array containing the color components
+ /**
+ * An array containing the color components
+ *
+ * @member {Float32Array}
*/
this.colors = new Float32Array(points.length * 2);
- /*
- * @member {Uint16Array} An array containing the indices of the vertices
+ /**
+ * An array containing the indices of the vertices
+ *
+ * @member {Uint16Array}
*/
this.indices = new Uint16Array(points.length * 2);
/**
- * Tracker for if the rope is ready to be drawn. Needed because Mesh ctor can
- * call _onTextureUpdated which could call refresh too early.
- *
+ * refreshes vertices on every updateTransform
* @member {boolean}
- * @private
+ * @default true
*/
- this._ready = true;
+ this.autoUpdate = true;
this.refresh();
}
@@ -67,7 +74,7 @@
* Refreshes
*
*/
- refresh()
+ _refresh()
{
const points = this.points;
@@ -91,14 +98,10 @@
const indices = this.indices;
const colors = this.colors;
- const textureUvs = this._texture._uvs;
- const offset = new core.Point(textureUvs.x0, textureUvs.y0);
- const factor = new core.Point(textureUvs.x2 - textureUvs.x0, Number(textureUvs.y2 - textureUvs.y0));
-
- uvs[0] = 0 + offset.x;
- uvs[1] = 0 + offset.y;
- uvs[2] = 0 + offset.x;
- uvs[3] = factor.y + offset.y;
+ uvs[0] = 0;
+ uvs[1] = 0;
+ uvs[2] = 0;
+ uvs[3] = 1;
colors[0] = 1;
colors[1] = 1;
@@ -114,11 +117,11 @@
let index = i * 4;
const amount = i / (total - 1);
- uvs[index] = (amount * factor.x) + offset.x;
- uvs[index + 1] = 0 + offset.y;
+ uvs[index] = amount;
+ uvs[index + 1] = 0;
- uvs[index + 2] = (amount * factor.x) + offset.x;
- uvs[index + 3] = factor.y + offset.y;
+ uvs[index + 2] = amount;
+ uvs[index + 3] = 1;
index = i * 2;
colors[index] = 1;
@@ -132,30 +135,15 @@
// ensure that the changes are uploaded
this.dirty++;
this.indexDirty++;
+
+ this.multiplyUvs();
+ this.refreshVertices();
}
/**
- * Clear texture UVs when new texture is set
- *
- * @private
+ * refreshes vertices of Rope mesh
*/
- _onTextureUpdate()
- {
- super._onTextureUpdate();
-
- // wait for the Rope ctor to finish before calling refresh
- if (this._ready)
- {
- this.refresh();
- }
- }
-
- /**
- * Updates the object transform for rendering
- *
- * @private
- */
- updateTransform()
+ refreshVertices()
{
const points = this.points;
@@ -214,7 +202,19 @@
lastPoint = point;
}
+ }
+ /**
+ * Updates the object transform for rendering
+ *
+ * @private
+ */
+ updateTransform()
+ {
+ if (this.autoUpdate)
+ {
+ this.refreshVertices();
+ }
this.containerUpdateTransform();
}
diff --git a/src/mesh/canvas/CanvasMeshRenderer.js b/src/mesh/canvas/CanvasMeshRenderer.js
index 1263170..5354e2c 100644
--- a/src/mesh/canvas/CanvasMeshRenderer.js
+++ b/src/mesh/canvas/CanvasMeshRenderer.js
@@ -135,12 +135,33 @@
const textureWidth = base.width;
const textureHeight = base.height;
- const u0 = uvs[index0] * base.width;
- const u1 = uvs[index1] * base.width;
- const u2 = uvs[index2] * base.width;
- const v0 = uvs[index0 + 1] * base.height;
- const v1 = uvs[index1 + 1] * base.height;
- const v2 = uvs[index2 + 1] * base.height;
+ let u0;
+ let u1;
+ let u2;
+ let v0;
+ let v1;
+ let v2;
+
+ if (mesh.uploadUvTransform)
+ {
+ const ut = mesh._uvTransform.mapCoord;
+
+ u0 = ((uvs[index0] * ut.a) + (uvs[index0 + 1] * ut.c) + ut.tx) * base.width;
+ u1 = ((uvs[index1] * ut.a) + (uvs[index1 + 1] * ut.c) + ut.tx) * base.width;
+ u2 = ((uvs[index2] * ut.a) + (uvs[index2 + 1] * ut.c) + ut.tx) * base.width;
+ v0 = ((uvs[index0] * ut.b) + (uvs[index0 + 1] * ut.d) + ut.ty) * base.height;
+ v1 = ((uvs[index1] * ut.b) + (uvs[index1 + 1] * ut.d) + ut.ty) * base.height;
+ v2 = ((uvs[index2] * ut.b) + (uvs[index2 + 1] * ut.d) + ut.ty) * base.height;
+ }
+ else
+ {
+ u0 = uvs[index0] * base.width;
+ u1 = uvs[index1] * base.width;
+ u2 = uvs[index2] * base.width;
+ v0 = uvs[index0 + 1] * base.height;
+ v1 = uvs[index1 + 1] * base.height;
+ v2 = uvs[index2 + 1] * base.height;
+ }
let x0 = vertices[index0];
let x1 = vertices[index1];
diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js
index 8e61919..9d59adb 100644
--- a/src/mesh/webgl/MeshRenderer.js
+++ b/src/mesh/webgl/MeshRenderer.js
@@ -4,6 +4,8 @@
import { readFileSync } from 'fs';
import { join } from 'path';
+const matrixIdentity = core.Matrix.IDENTITY;
+
/**
* WebGL renderer plugin for tiling sprites
*
@@ -104,6 +106,17 @@
renderer.state.setBlendMode(mesh.blendMode);
+ if (glData.shader.uniforms.uTransform)
+ {
+ if (mesh.uploadUvTransform)
+ {
+ glData.shader.uniforms.uTransform = mesh._uvTransform.mapCoord.toArray(true);
+ }
+ else
+ {
+ glData.shader.uniforms.uTransform = matrixIdentity.toArray(true);
+ }
+ }
glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true);
glData.shader.uniforms.alpha = mesh.worldAlpha;
glData.shader.uniforms.tint = mesh.tintRgb;
diff --git a/src/mesh/webgl/mesh.vert b/src/mesh/webgl/mesh.vert
index a337aef..acc096c 100644
--- a/src/mesh/webgl/mesh.vert
+++ b/src/mesh/webgl/mesh.vert
@@ -1,8 +1,9 @@
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
-uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
+uniform mat3 translationMatrix;
+uniform mat3 uTransform;
varying vec2 vTextureCoord;
@@ -10,5 +11,5 @@
{
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
- vTextureCoord = aTextureCoord;
+ vTextureCoord = (uTransform * vec3(aTextureCoord, 1.0)).xy;
}
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 67796cf..63f7791 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -313,10 +313,10 @@
frame.y * resolution,
frame.width * resolution,
frame.height * resolution,
- positionX * resolution,
- positionY * resolution,
- finalWidth * resolution,
- finalHeight * resolution
+ positionX * renderer.resolution,
+ positionY * renderer.resolution,
+ finalWidth * renderer.resolution,
+ finalHeight * renderer.resolution
);
}
}
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index a4aae2f..e45c4dc 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -18,6 +18,18 @@
* basic queuing functionality and is extended by {@link PIXI.prepare.WebGLPrepare} and {@link PIXI.prepare.CanvasPrepare}
* to provide preparation capabilities specific to their respective renderers.
*
+ * @example
+ * // Create a sprite
+ * const sprite = new PIXI.Sprite.fromImage('something.png');
+ *
+ * // Load object into GPU
+ * app.renderer.plugins.prepare.upload(sprite, () => {
+ *
+ * //Texture(s) has been uploaded to GPU
+ * app.stage.addChild(sprite);
+ *
+ * })
+ *
* @abstract
* @class
* @memberof PIXI.prepare
@@ -100,8 +112,16 @@
this.prepareItems();
};
- this.register(findText, drawText);
- this.register(findTextStyle, calculateTextStyle);
+ // hooks to find the correct texture
+ this.registerFindHook(findText);
+ this.registerFindHook(findTextStyle);
+ this.registerFindHook(findMultipleBaseTextures);
+ this.registerFindHook(findBaseTexture);
+ this.registerFindHook(findTexture);
+
+ // upload hooks
+ this.registerUploadHook(drawText);
+ this.registerUploadHook(calculateTextStyle);
}
/**
@@ -138,7 +158,7 @@
if (!this.ticking)
{
this.ticking = true;
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
else if (done)
@@ -208,26 +228,36 @@
else
{
// if we are not finished, on the next rAF do this again
- SharedTicker.addOnce(this.tick, this);
+ SharedTicker.addOnce(this.tick, this, core.UPDATE_PRIORITY.UTILITY);
}
}
/**
- * Adds hooks for finding and uploading items.
+ * Adds hooks for finding items.
*
- * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array`
- function must return `true` if it was able to add item to the queue.
- * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
- * function must return `true` if it was able to handle upload of item.
- * @return {PIXI.CanvasPrepare} Instance of plugin for chaining.
+ * @param {Function} addHook - Function call that takes two parameters: `item:*, queue:Array`
+ * function must return `true` if it was able to add item to the queue.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
*/
- register(addHook, uploadHook)
+ registerFindHook(addHook)
{
if (addHook)
{
this.addHooks.push(addHook);
}
+ return this;
+ }
+
+ /**
+ * Adds hooks for uploading items.
+ *
+ * @param {Function} uploadHook - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and
+ * function must return `true` if it was able to handle upload of item.
+ * @return {PIXI.BasePrepare} Instance of plugin for chaining.
+ */
+ registerUploadHook(uploadHook)
+ {
if (uploadHook)
{
this.uploadHooks.push(uploadHook);
@@ -290,6 +320,88 @@
}
/**
+ * Built-in hook to find multiple textures from objects like AnimatedSprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findMultipleBaseTextures(item, queue)
+{
+ let result = false;
+
+ // Objects with mutliple textures
+ if (item && item._textures && item._textures.length)
+ {
+ for (let i = 0; i < item._textures.length; i++)
+ {
+ if (item._textures[i] instanceof core.Texture)
+ {
+ const baseTexture = item._textures[i].baseTexture;
+
+ if (queue.indexOf(baseTexture) === -1)
+ {
+ queue.push(baseTexture);
+ result = true;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Built-in hook to find BaseTextures from Sprites.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findBaseTexture(item, queue)
+{
+ // Objects with textures, like Sprites/Text
+ if (item instanceof core.BaseTexture)
+ {
+ if (queue.indexOf(item) === -1)
+ {
+ queue.push(item);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Built-in hook to find textures from objects.
+ *
+ * @private
+ * @param {PIXI.DisplayObject} item - Display object to check
+ * @param {Array<*>} queue - Collection of items to upload
+ * @return {boolean} if a PIXI.Texture object was found.
+ */
+function findTexture(item, queue)
+{
+ if (item._texture && item._texture instanceof core.Texture)
+ {
+ const texture = item._texture.baseTexture;
+
+ if (queue.indexOf(texture) === -1)
+ {
+ queue.push(texture);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Built-in hook to draw PIXI.Text to its texture.
*
* @private
diff --git a/src/prepare/canvas/CanvasPrepare.js b/src/prepare/canvas/CanvasPrepare.js
index 0224bb5..09c207a 100644
--- a/src/prepare/canvas/CanvasPrepare.js
+++ b/src/prepare/canvas/CanvasPrepare.js
@@ -43,7 +43,7 @@
this.ctx = this.canvas.getContext('2d');
// Add textures to upload
- this.register(findBaseTextures, uploadBaseTextures);
+ this.registerUploadHook(uploadBaseTextures);
}
/**
@@ -89,39 +89,4 @@
return false;
}
-/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item -Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
core.CanvasRenderer.registerPlugin('prepare', CanvasPrepare);
diff --git a/src/prepare/index.js b/src/prepare/index.js
index f559c45..e7491ec 100644
--- a/src/prepare/index.js
+++ b/src/prepare/index.js
@@ -1,4 +1,29 @@
/**
+ * The prepare namespace provides renderer-specific plugins for pre-rendering DisplayObjects. These plugins are useful for
+ * asynchronously preparing assets, textures, graphics waiting to be displayed.
+ *
+ * Do not instantiate these plugins directly. It is available from the `renderer.plugins` property.
+ * See {@link PIXI.CanvasRenderer#plugins} or {@link PIXI.WebGLRenderer#plugins}.
+ * @example
+ * // Create a new application
+ * const app = new PIXI.Application();
+ * document.body.appendChild(app.view);
+ *
+ * // Don't start rendering right away
+ * app.stop();
+ *
+ * // create a display object
+ * const rect = new PIXI.Graphics()
+ * .beginFill(0x00ff00)
+ * .drawRect(40, 40, 200, 200);
+ *
+ * // Add to the stage
+ * app.stage.addChild(rect);
+ *
+ * // Don't start rendering until the graphic is uploaded to the GPU
+ * app.renderer.plugins.prepare.upload(app.stage, () => {
+ * app.start();
+ * });
* @namespace PIXI.prepare
*/
export { default as webgl } from './webgl/WebGLPrepare';
diff --git a/src/prepare/webgl/WebGLPrepare.js b/src/prepare/webgl/WebGLPrepare.js
index eb7023f..9df3c3f 100644
--- a/src/prepare/webgl/WebGLPrepare.js
+++ b/src/prepare/webgl/WebGLPrepare.js
@@ -22,12 +22,11 @@
this.uploadHookHelper = this.renderer;
// Add textures and graphics to upload
- this.register(findBaseTextures, uploadBaseTextures)
- .register(findGraphics, uploadGraphics);
+ this.registerFindHook(findGraphics);
+ this.registerUploadHook(uploadBaseTextures);
+ this.registerUploadHook(uploadGraphics);
}
-
}
-
/**
* Built-in hook to upload PIXI.Texture objects to the GPU.
*
@@ -80,41 +79,6 @@
}
/**
- * Built-in hook to find textures from Sprites.
- *
- * @private
- * @param {PIXI.DisplayObject} item - Display object to check
- * @param {Array<*>} queue - Collection of items to upload
- * @return {boolean} if a PIXI.Texture object was found.
- */
-function findBaseTextures(item, queue)
-{
- // Objects with textures, like Sprites/Text
- if (item instanceof core.BaseTexture)
- {
- if (queue.indexOf(item) === -1)
- {
- queue.push(item);
- }
-
- return true;
- }
- else if (item._texture && item._texture instanceof core.Texture)
- {
- const texture = item._texture.baseTexture;
-
- if (queue.indexOf(texture) === -1)
- {
- queue.push(texture);
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Built-in hook to find graphics.
*
* @private
diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js
index 8b93478..ac44c6a 100644
--- a/test/core/BaseTexture.js
+++ b/test/core/BaseTexture.js
@@ -1,11 +1,28 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('BaseTexture', function ()
{
describe('updateImageType', function ()
{
it('should allow no extension', function ()
{
+ cleanCache();
+
const baseTexture = new PIXI.BaseTexture();
baseTexture.imageUrl = 'http://some.domain.org/100/100';
@@ -14,4 +31,74 @@
expect(baseTexture.imageType).to.be.equals('png');
});
});
+
+ it('should remove Canvas BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const canvas = document.createElement('canvas');
+ const baseTexture = PIXI.BaseTexture.fromCanvas(canvas);
+ const _pixiId = canvas._pixiId;
+
+ expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture);
+ baseTexture.destroy();
+ expect(baseTexture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined);
+ });
+
+ it('should remove Image BaseTexture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const image = new Image();
+
+ const texture = PIXI.Texture.fromLoader(image, URL, NAME);
+
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ texture.destroy(true);
+ expect(texture.baseTexture).to.equal(null);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(baseTexture);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const baseTexture = new PIXI.BaseTexture();
+
+ PIXI.BaseTexture.addToCache(baseTexture, NAME);
+ PIXI.BaseTexture.addToCache(baseTexture, NAME2);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ PIXI.BaseTexture.removeFromCache(NAME);
+ expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture);
+ });
});
diff --git a/test/core/Bounds.js b/test/core/Bounds.js
index d2893a6..454a47c 100644
--- a/test/core/Bounds.js
+++ b/test/core/Bounds.js
@@ -350,7 +350,7 @@
expect(bounds.height).to.equal(20);
});
- it('should register correct width/height with an a Text Object', function ()
+ it('should register correct width/height with a Text Object', function ()
{
const parent = new PIXI.Container();
diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js
index 283adf5..7a3d6ac 100644
--- a/test/core/Spritesheet.js
+++ b/test/core/Spritesheet.js
@@ -18,6 +18,7 @@
expect(textures[id]).to.be.an.instanceof(PIXI.Texture);
expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution);
expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution);
+ expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0);
spritesheet.destroy(true);
expect(spritesheet.textures).to.be.null;
expect(spritesheet.baseTexture).to.be.null;
diff --git a/test/core/Text.js b/test/core/Text.js
index 5799ad4..e678ba4 100644
--- a/test/core/Text.js
+++ b/test/core/Text.js
@@ -2,40 +2,6 @@
describe('PIXI.Text', function ()
{
- describe('getFontStyle', function ()
- {
- it('should be a valid API', function ()
- {
- expect(PIXI.Text.getFontStyle).to.be.a.function;
- });
-
- it('should assume pixel fonts', function ()
- {
- const style = PIXI.Text.getFontStyle({ fontSize: 72 });
-
- expect(style).to.be.a.string;
- expect(style).to.have.string(' 72px ');
- });
-
- it('should handle multiple fonts as array', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: ['Georgia', 'Arial', 'sans-serif'],
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
-
- it('should handle multiple fonts as string', function ()
- {
- const style = PIXI.Text.getFontStyle({
- fontFamily: 'Georgia, "Arial", sans-serif',
- });
-
- expect(style).to.have.string('"Georgia","Arial","sans-serif"');
- });
- });
-
describe('destroy', function ()
{
it('should call through to Sprite.destroy', function ()
diff --git a/test/core/TextStyle.js b/test/core/TextStyle.js
index b5761ad..49b99ef 100644
--- a/test/core/TextStyle.js
+++ b/test/core/TextStyle.js
@@ -23,4 +23,31 @@
expect(textStyle.fontSize).to.equal(1000);
expect(clonedTextStyle.fontSize).to.equal(textStyle.fontSize);
});
+
+ it('should assume pixel fonts', function ()
+ {
+ const style = new PIXI.TextStyle({ fontSize: 72 });
+ const font = style.toFontString();
+
+ expect(font).to.be.a.string;
+ expect(font).to.have.string(' 72px ');
+ });
+
+ it('should handle multiple fonts as array', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: ['Georgia', 'Arial', 'sans-serif'],
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
+
+ it('should handle multiple fonts as string', function ()
+ {
+ const style = new PIXI.TextStyle({
+ fontFamily: 'Georgia, "Arial", sans-serif',
+ });
+
+ expect(style.toFontString()).to.have.string('"Georgia","Arial","sans-serif"');
+ });
});
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 8c4ef98..995ed40 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -1,11 +1,26 @@
'use strict';
+const URL = 'foo.png';
+const NAME = 'foo';
+const NAME2 = 'bar';
+
+function cleanCache()
+{
+ delete PIXI.utils.BaseTextureCache[URL];
+ delete PIXI.utils.BaseTextureCache[NAME];
+ delete PIXI.utils.BaseTextureCache[NAME2];
+
+ delete PIXI.utils.TextureCache[URL];
+ delete PIXI.utils.TextureCache[NAME];
+ delete PIXI.utils.TextureCache[NAME2];
+}
+
describe('PIXI.Texture', function ()
{
it('should register Texture from Loader', function ()
{
- const URL = 'foo.png';
- const NAME = 'bar';
+ cleanCache();
+
const image = new Image();
const texture = PIXI.Texture.fromLoader(image, URL, NAME);
@@ -16,4 +31,100 @@
expect(PIXI.utils.TextureCache[URL]).to.equal(texture);
expect(PIXI.utils.BaseTextureCache[URL]).to.equal(texture.baseTexture);
});
+
+ it('should remove Texture from cache on destroy', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ texture.destroy();
+ expect(texture.textureCacheIds).to.equal(null);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should be added to the texture cache correctly, '
+ + 'and should remove only itself, not effecting the base texture and its cache', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
+ PIXI.Texture.addToCache(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.removeFromCache(NAME);
+ expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
+ 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();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(texture);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined);
+ });
+
+ it('should remove Texture from single cache entry using removeFromCache (by id)', function ()
+ {
+ cleanCache();
+
+ const texture = new PIXI.Texture(new PIXI.BaseTexture());
+
+ PIXI.Texture.addToCache(texture, NAME);
+ PIXI.Texture.addToCache(texture, NAME2);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ PIXI.Texture.removeFromCache(NAME);
+ expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
+ expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0);
+ expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
+ expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture);
+ });
});
diff --git a/test/core/Ticker.js b/test/core/Ticker.js
new file mode 100644
index 0000000..188770f
--- /dev/null
+++ b/test/core/Ticker.js
@@ -0,0 +1,322 @@
+'use strict';
+
+const Ticker = PIXI.ticker.Ticker;
+const shared = PIXI.ticker.shared;
+
+describe('PIXI.ticker.Ticker', function ()
+{
+ before(function ()
+ {
+ this.length = (ticker) =>
+ {
+ ticker = ticker || shared;
+
+ if (!ticker._head || !ticker._head.next)
+ {
+ return 0;
+ }
+
+ let listener = ticker._head.next;
+ let i = 0;
+
+ while (listener)
+ {
+ listener = listener.next;
+ i++;
+ }
+
+ return i;
+ };
+ });
+
+ it('should be available', function ()
+ {
+ expect(Ticker).to.be.a.function;
+ expect(shared).to.be.an.instanceof(Ticker);
+ });
+
+ it('should create a new ticker and destroy it', function ()
+ {
+ const ticker = new Ticker();
+
+ ticker.start();
+
+ const listener = sinon.spy();
+
+ expect(this.length(ticker)).to.equal(0);
+
+ ticker.add(listener);
+
+ expect(this.length(ticker)).to.equal(1);
+
+ ticker.destroy();
+
+ expect(ticker._head).to.be.null;
+ expect(ticker.started).to.be.false;
+ expect(this.length(ticker)).to.equal(0);
+ });
+
+ it('should protect destroying shared ticker', function ()
+ {
+ shared.destroy();
+ expect(shared._head).to.not.be.null;
+ expect(shared.started).to.be.true;
+ });
+
+ it('should add and remove listener', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener);
+
+ expect(this.length()).to.equal(length + 1);
+
+ shared.remove(listener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should update a listener', function ()
+ {
+ const listener = sinon.spy();
+
+ shared.add(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ });
+
+ it('should update a listener twice and remove once', function ()
+ {
+ const listener = sinon.spy();
+ const length = this.length();
+
+ shared.add(listener).add(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length + 2);
+
+ shared.remove(listener);
+ shared.update();
+
+ expect(listener.calledTwice).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should respect priority order', function ()
+ {
+ const length = this.length();
+ const listener1 = sinon.spy();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.LOW)
+ .add(listener4, null, PIXI.UPDATE_PRIORITY.INTERACTION)
+ .add(listener3, null, PIXI.UPDATE_PRIORITY.HIGH)
+ .add(listener2, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 4);
+
+ sinon.assert.callOrder(listener4, listener3, listener2, listener1);
+
+ shared.remove(listener1)
+ .remove(listener2)
+ .remove(listener3)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should auto-remove once listeners', function ()
+ {
+ const length = this.length();
+ const listener = sinon.spy();
+
+ shared.addOnce(listener);
+
+ shared.update();
+
+ expect(listener.calledOnce).to.be.true;
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should call inserted item with a lower priority', function ()
+ {
+ const length = this.length();
+ const lowListener = sinon.spy();
+ const highListener = sinon.spy();
+ const mainListener = sinon.spy(() =>
+ {
+ shared.add(highListener, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(lowListener, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(mainListener, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 3);
+
+ expect(mainListener.calledOnce).to.be.true;
+ expect(lowListener.calledOnce).to.be.true;
+ expect(highListener.calledOnce).to.be.false;
+
+ shared.remove(mainListener)
+ .remove(highListener)
+ .remove(lowListener);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove emit low-priority item during emit', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself on emit after adding new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.remove(listener1);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove itself before, still calling new item', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener1);
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 1);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 1);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.remove(listener2);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should remove items before and after current priority', function ()
+ {
+ const length = this.length();
+ const listener2 = sinon.spy();
+ const listener3 = sinon.spy();
+ const listener4 = sinon.spy();
+
+ shared.add(listener2, null, PIXI.UPDATE_PRIORITY.HIGH);
+ shared.add(listener3, null, PIXI.UPDATE_PRIORITY.LOW);
+ shared.add(listener4, null, PIXI.UPDATE_PRIORITY.LOW);
+
+ const listener1 = sinon.spy(() =>
+ {
+ shared.remove(listener2)
+ .remove(listener3);
+
+ // listener is removed right away
+ expect(this.length()).to.equal(length + 2);
+ });
+
+ shared.add(listener1, null, PIXI.UPDATE_PRIORITY.NORMAL);
+
+ shared.update();
+
+ expect(this.length()).to.equal(length + 2);
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledOnce).to.be.true;
+ expect(listener1.calledOnce).to.be.true;
+
+ shared.update();
+
+ expect(listener2.calledOnce).to.be.true;
+ expect(listener3.calledOnce).to.be.false;
+ expect(listener4.calledTwice).to.be.true;
+ expect(listener1.calledTwice).to.be.true;
+
+ shared.remove(listener1)
+ .remove(listener4);
+
+ expect(this.length()).to.equal(length);
+ });
+
+ it('should destroy on listener', function (done)
+ {
+ const ticker = new Ticker();
+ const listener2 = sinon.spy();
+ const listener = sinon.spy(() =>
+ {
+ ticker.destroy();
+ setTimeout(() =>
+ {
+ expect(listener2.called).to.be.false;
+ expect(listener.calledOnce).to.be.true;
+ done();
+ }, 0);
+ });
+
+ ticker.add(listener);
+ ticker.add(listener2, null, PIXI.UPDATE_PRIORITY.LOW);
+ ticker.start();
+ });
+});
diff --git a/test/core/index.js b/test/core/index.js
index 8530131..28636ab 100755
--- a/test/core/index.js
+++ b/test/core/index.js
@@ -17,6 +17,7 @@
require('./util');
require('./Plane');
require('./Point');
+require('./Polygon');
require('./ObservablePoint');
require('./Matrix');
require('./Rectangle');
@@ -26,5 +27,7 @@
require('./SpriteRenderer');
require('./WebGLRenderer');
require('./Ellipse');
+require('./BaseTexture');
require('./Texture');
+require('./Ticker');
require('./filters');
diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js
index c87fa5c..31385b9 100644
--- a/test/interaction/InteractionManager.js
+++ b/test/interaction/InteractionManager.js
@@ -1198,4 +1198,85 @@
expect(pointer.interaction.activeInteractionData[42]).to.be.undefined;
});
});
+
+ describe('hitTest()', function ()
+ {
+ it('should return hit', 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.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10));
+
+ expect(hit).to.equal(graphics);
+ });
+
+ it('should return null if not hit', 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.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(60, 60));
+
+ expect(hit).to.be.null;
+ });
+
+ it('should return top thing that was hit', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const behind = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(behind);
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ behind.beginFill(0xFFFFFF);
+ behind.drawRect(0, 0, 50, 50);
+ behind.interactive = true;
+
+ pointer.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10));
+
+ expect(hit).to.equal(graphics);
+ });
+
+ it('should return hit when passing in root', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const behind = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(behind);
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ behind.beginFill(0xFFFFFF);
+ behind.drawRect(0, 0, 50, 50);
+ behind.interactive = true;
+
+ pointer.render();
+ const hit = pointer.interaction.hitTest(new PIXI.Point(10, 10), behind);
+
+ expect(hit).to.equal(behind);
+ });
+ });
});
diff --git a/test/loaders/spritesheetParser.js b/test/loaders/spritesheetParser.js
index 79f8c15..4d54805 100644
--- a/test/loaders/spritesheetParser.js
+++ b/test/loaders/spritesheetParser.js
@@ -60,11 +60,46 @@
.that.is.an.instanceof(PIXI.Texture);
});
+ it('should build the image url', function ()
+ {
+ function getResourcePath(url, image)
+ {
+ return PIXI.loaders.getResourcePath({
+ url,
+ data: { meta: { image } },
+ });
+ }
+
+ let result = getResourcePath('http://some.com/spritesheet.json', 'img.png');
+
+ expect(result).to.be.equals('http://some.com/img.png');
+
+ result = getResourcePath('http://some.com/some/dir/spritesheet.json', 'img.png');
+ expect(result).to.be.equals('http://some.com/some/dir/img.png');
+
+ result = getResourcePath('http://some.com/some/dir/spritesheet.json', './img.png');
+ expect(result).to.be.equals('http://some.com/some/dir/img.png');
+
+ result = getResourcePath('http://some.com/some/dir/spritesheet.json', '../img.png');
+ expect(result).to.be.equals('http://some.com/some/img.png');
+
+ result = getResourcePath('/spritesheet.json', 'img.png');
+ expect(result).to.be.equals('/img.png');
+
+ result = getResourcePath('/some/dir/spritesheet.json', 'img.png');
+ expect(result).to.be.equals('/some/dir/img.png');
+
+ result = getResourcePath('/some/dir/spritesheet.json', './img.png');
+ expect(result).to.be.equals('/some/dir/img.png');
+
+ result = getResourcePath('/some/dir/spritesheet.json', '../img.png');
+ expect(result).to.be.equals('/some/img.png');
+ });
+
// TODO: Test that rectangles are created correctly.
// TODO: Test that bathc processing works correctly.
// TODO: Test that resolution processing works correctly.
// TODO: Test that metadata is honored.
- // TODO: Test data-url code paths.
});
function createMockResource(type, data)
diff --git a/test/prepare/BasePrepare.js b/test/prepare/BasePrepare.js
index 3cfa476..26dc8ad 100644
--- a/test/prepare/BasePrepare.js
+++ b/test/prepare/BasePrepare.js
@@ -10,7 +10,7 @@
expect(prep.renderer).to.equal(renderer);
expect(prep.uploadHookHelper).to.be.null;
expect(prep.queue).to.be.empty;
- expect(prep.addHooks).to.have.lengthOf(2);
+ expect(prep.addHooks).to.have.lengthOf(5);
expect(prep.uploadHooks).to.have.lengthOf(2);
expect(prep.completes).to.be.empty;
@@ -23,10 +23,11 @@
function uploadHook() { /* empty */ }
const prep = new PIXI.prepare.BasePrepare();
- prep.register(addHook, uploadHook);
+ prep.registerFindHook(addHook);
+ prep.registerUploadHook(uploadHook);
expect(prep.addHooks).to.contain(addHook);
- expect(prep.addHooks).to.have.lengthOf(3);
+ expect(prep.addHooks).to.have.lengthOf(6);
expect(prep.uploadHooks).to.contain(uploadHook);
expect(prep.uploadHooks).to.have.lengthOf(3);
@@ -58,7 +59,8 @@
});
const complete = sinon.spy(function () { /* empty */ });
- prep.register(addHook, uploadHook);
+ prep.registerFindHook(addHook);
+ prep.registerUploadHook(uploadHook);
prep.upload(uploadItem, complete);
expect(prep.queue).to.contain(uploadItem);
@@ -82,7 +84,7 @@
}
const complete = sinon.spy(function () { /* empty */ });
- prep.register(addHook);
+ prep.registerFindHook(addHook);
prep.upload({}, complete);
expect(complete.calledOnce).to.be.true;
@@ -106,7 +108,8 @@
});
const complete = sinon.spy(function () { /* empty */ });
- prep.register(addHook, uploadHook);
+ prep.registerFindHook(addHook);
+ prep.registerUploadHook(uploadHook);
prep.upload({}, complete);
expect(prep.queue).to.have.lengthOf(1);
@@ -137,7 +140,8 @@
});
const complete = sinon.spy(function () { /* empty */ });
- prep.register(addHook, uploadHook);
+ prep.registerFindHook(addHook);
+ prep.registerUploadHook(uploadHook);
const item = {};
prep.upload(item, complete);
@@ -181,7 +185,8 @@
done();
}
- prep.register(addHook, uploadHook);
+ prep.registerFindHook(addHook);
+ prep.registerUploadHook(uploadHook);
prep.upload({}, complete);
expect(prep.queue).to.have.lengthOf(1);